#include <string>
#include <regex>
#include <iostream>
#include <sstream>
#include <optional>
#include <cmath>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "tyme.h"

using std::ceil;

using namespace std;

namespace tyme {
    bool AbstractCulture::equals(const Culture *o) const {
        return o && o->to_string() == to_string();
    }

    string AbstractCulture::get_name() const {
        return "";
    }

    string AbstractCulture::to_string() const {
        return get_name();
    }

    int AbstractCulture::index_of(const int index, const int size) {
        const int i = index % size;
        return i < 0 ? i + size : i;
    }

    int LoopTyme::get_index() const {
        return index;
    }

    int LoopTyme::get_size() const {
        return static_cast<int>(names.size());
    }

    string LoopTyme::get_name() const {
        return names[index];
    }

    int LoopTyme::index_of(const string &name) const {
        for (int i = 0, j = get_size(); i < j; i++) {
            if (name == names[i]) {
                return i;
            }
        }
        throw invalid_argument("illegal name: " + name);
    }

    int LoopTyme::index_of(const int index) const {
        return AbstractCulture::index_of(index, get_size());
    }

    int LoopTyme::next_index(const int n) const {
        return index_of(index + n);
    }

    int LoopTyme::steps_to(const int target_index) const {
        return index_of(target_index - index);
    }

    const vector<string> Animal::NAMES = {
        "蛟", "龙", "貉", "兔", "狐", "虎", "豹", "獬", "牛", "蝠", "鼠", "燕", "猪", "獝", "狼", "狗", "彘", "鸡", "乌", "猴", "猿", "犴", "羊", "獐", "马", "鹿", "蛇", "蚓"
    };

    Animal Animal::from_index(const int index) {
        return Animal(index);
    }

    Animal Animal::from_name(const string &name) {
        return Animal(name);
    }

    Animal Animal::next(const int n) const {
        return from_index(next_index(n));
    }

    TwentyEightStar Animal::get_twenty_eight_star() const {
        return TwentyEightStar::from_index(index);
    }

    const vector<string> Beast::NAMES = {
        "青龙", "玄武", "白虎", "朱雀"
    };

    Beast Beast::from_index(const int index) {
        return Beast(index);
    }

    Beast Beast::from_name(const string &name) {
        return Beast(name);
    }

    Beast Beast::next(const int n) const {
        return from_index(next_index(n));
    }

    Zone Beast::get_zone() const {
        return Zone::from_index(index);
    }

    const vector<string> Constellation::NAMES = {
        "白羊", "金牛", "双子", "巨蟹", "狮子", "处女", "天秤", "天蝎", "射手", "摩羯", "水瓶", "双鱼"
    };

    Constellation Constellation::from_index(const int index) {
        return Constellation(index);
    }

    Constellation Constellation::from_name(const string &name) {
        return Constellation(name);
    }

    Constellation Constellation::next(const int n) const {
        return from_index(next_index(n));
    }

    /**
     * @brief 依据后天八卦排序（0坎北, 1坤西南, 2震东, 3巽东南, 4中, 5乾西北, 6兑西, 7艮东北, 8离南）
     */
    const vector<string> Direction::NAMES = {
        "北", "西南", "东", "东南", "中", "西北", "西", "东北", "南"
    };

    Direction Direction::from_index(const int index) {
        return Direction(index);
    }

    Direction Direction::from_name(const string &name) {
        return Direction(name);
    }

    Direction Direction::next(const int n) const {
        return from_index(next_index(n));
    }

    Land Direction::get_land() const {
        return Land::from_index(index);
    }

    Element Direction::get_element() const {
        const int indices[9] = {4, 2, 0, 0, 2, 3, 3, 2, 1};
        return Element::from_index(indices[index]);
    }

    const vector<string> Duty::NAMES = {
        "建", "除", "满", "平", "定", "执", "破", "危", "成", "收", "开", "闭"
    };

    Duty Duty::from_index(const int index) {
        return Duty(index);
    }

    Duty Duty::from_name(const string &name) {
        return Duty(name);
    }

    Duty Duty::next(const int n) const {
        return from_index(next_index(n));
    }

    const vector<string> Element::NAMES = {
        "木", "火", "土", "金", "水"
    };

    Element Element::from_index(const int index) {
        return Element(index);
    }

    Element Element::from_name(const string &name) {
        return Element(name);
    }

    Element Element::next(const int n) const {
        return from_index(next_index(n));
    }

    bool Element::equals(const Element &other) const {
        return to_string() == other.to_string();
    }


    Element Element::get_reinforce() const {
        return next(1);
    }

    Element Element::get_restrain() const {
        return next(2);
    }

    Element Element::get_reinforced() const {
        return next(-1);
    }

    Element Element::get_restrained() const {
        return next(-2);
    }

    Direction Element::get_direction() const {
        const int indices[5] = {2, 8, 4, 6, 0};
        return Direction::from_index(indices[index]);
    }

    const vector<string> God::NAMES = {
        "天恩", "鸣吠", "母仓", "不将", "四相", "鸣吠对", "五合", "三合", "除神", "月德", "月空", "月德合", "月恩", "时阴", "五富", "生气", "金匮", "相日", "阴德", "六合", "益后", "青龙", "续世", "明堂", "王日", "要安", "官日", "吉期", "福德", "六仪", "金堂", "宝光", "民日", "临日", "天马", "敬安", "普护", "驿马", "天后", "阳德", "天喜", "天医", "司命", "圣心", "玉宇", "守日", "时德", "解神", "时阳", "天仓", "天巫", "玉堂", "福生", "天德", "天德合", "天愿", "天赦", "天符", "阴神", "解除", "五虚", "五离", "重日", "复日", "血支", "天贼", "土符", "游祸", "白虎", "小耗", "致死", "河魁", "劫煞", "月煞", "月建", "往亡", "大时", "大败", "咸池", "厌对", "招摇", "九坎", "九焦", "天罡", "死神", "月害", "死气", "月破", "大耗", "天牢", "元武", "月厌", "月虚", "归忌", "小时", "天刑", "朱雀", "九空", "天吏", "地火", "四击", "大煞", "勾陈", "八专", "灾煞", "天火", "血忌", "土府", "月刑", "触水龙", "地囊", "八风", "四废", "四忌", "四穷", "五墓", "阴错", "四耗", "阳错", "孤辰", "小会", "大会", "八龙", "七鸟", "九虎", "六蛇", "天狗", "行狠", "了戾", "岁薄", "逐阵", "三丧", "三阴", "阴道冲阳", "阴位", "阴阳交破", "阴阳俱错", "阴阳击冲", "鬼哭", "单阴", "绝阴", "纯阳", "阳错阴冲", "七符", "成日", "孤阳", "绝阳", "纯阴", "大退", "四离", "阳破阴冲"
    };

    const vector<string> God::DAY_GODS = {
        ";000002300F14156869717A3F;01001617495C40413C425D6A;0209000C041831031906054A5E6B4B5F;033500041A1B032C06054C4D4E60;04002D321C1D1E104F50615152;05111F53546C55433C3E;062E200721220D01566E44;070B2333242F45;08360A2526242F080157583D59;091234080162463C3D5A;0A270728292A5B6364653F79;0B0237130E2B4748727A3E66;0C09020C04300F0314150568696D;0D3504031617495C40413C6F425D6A;0E38183119064A5E6B4B5F;0F001A1B032C064C4D4E60;10002D321C1D1E104F50615152;110B00111F53546C55433C3E;12360A002E200721220D015644;13002333456D;142526242F080157583F3D59;15001234080162463C3D5A;16090004270728292A5B636465;17350204130E032B47483E66;1802300F14156869;19031617495C40413C425D6A;1A1831031906054A5E6B4B5F;1B0B1A1B032C06054C4D4E;1C360A2D321C1D1E104F50615152;1D111F53546C55433C3E;1E2E200721220D01563F44;1F23334573;20090C042526242F080157583D;2135041234080162463C3D5A;22270728292A5B636465;2302130E032B47483E66;2402300F0314150568696E;250B031617495C40413C425D6A;26360A18311906054A5E6B4B5F;271A1B2C06054C4D4E60;282D321C1D1E104F506151523F;29111F53546C55433C3E;2A090C042E200721220D015644;2B350423334567;2C2526242F0857583D59;2D001234080162463C3D5A;2E00270728292A5B63646574;2F0B0002130E032B47483E66;30360A0002300F141505686975;31001617495C40413C425D6A676D;3218311906054A5E6B4B3F675F76;331A1B2C06054C4D4E60;34090C042D321C1D1E104F50615152;353504111F53546C55433C6F3E;362E200721220D5644;3723334567;382526242F08015758703D6759;390B123408016246703C3D5A84;3A360A270728292A5B636465;3B02130E2B47483E66;",
        ";00090002272A536C4C4D4E41717A;0100300F3103233C6151523F66;020004180E032406150543405D;03000C041A1D340617054A5E6B4F50;04002D1B555F;050B112526321C2B3C42654B3E60;060A2E2014100547546246;0712070D161F566A;0822192F0148453D44;092C083301575868695B633C3D;0A0937131E495C6459;0B020721282903727A3F3E5A;0C020427032A05536C4C4D4E416D;0D0C04300F03233C6F61515266;0E38180E24061543405D;0F0B001A1D3406174A5E6B4F5078;100A002D1B555F;1100112526321C2B3C42654B3E60;12002E2014100147546246;130012070D161F566A6D;140922192F080148453D44;152C083301575868695B633C3F3D44;160413031E495C6459;17020C0407212829033E5A;1802272A536C4C4D4E41;190B300F3103233C61515266;1A0A180E032406150543405D;1B1A1D340617014A5E6B4F50;1C2D1B555F;1D112526321C2B3C42654B3E60;1E092E2014100147546246;1F12070D161F56736E6A3F;200422192F080148453D44;210C042C083301575868695B633C3D;22131E495C6459;230B0207212829033E5A;240A0227032A05536C4C4D4E41;25300F31233C61515266;26180E2406150543405D;271A1D340617054A5E6B4F50;28092D1B555F;29112526321C2B3C42654B3F3E60;2A042E2014100147546246;2B0C0412070D161F566A67;2C22192F0848453D44;2D0B002C083301575868695B633C3D85;2E0A0013031E495C6459;2F0002072128293E5A;300002272A05536C4C4D4E4175;3100300F31233C6151526E676D66;3209180E2406150543405D;331A1D340617054A5E6B4F503F76;34042D1B555F;350C04112526321C2B3C6F42654B3E60;362E20141047546246;370B12070D161F566A67;380A22192F08014845703D6744;392C083301575868695B63703C3D74;3A131E495C6459;3B02072128293E5A;",
        ";00000207282931032B717A6E5D59;01000314473C5A;020A000427182526300F1D16062A054F506A;03360B00041A1906055562464066;04002D2C154A5E6B6C733F788B;0512111B0E1E17483C3E;060C2E20321C016869655F;0753544960;08350907210D230810015B63564B3D77;091324081F014C4D4E453C423D;0A2203342F57586461515244;0B02032C4341727A3E;0C0A020407282931032B055D6D59;0D360B040314473C6F5A;0E3827182526300F1D16062A4F506A3F;0F001A19065562464066;10000C2D2C154A5E6B6C86;110012111B0E1E17483C3E;123509002E20321C0168696E655F;13005354495C6D60;1407210D230810015B63564B3D7F;1537130324081F014C4D4E453C423D;160A042203342F57586461515244;17360B0204033343413E;1802072829312B5D3F59;190314473C5A;1A0C27182526300F1D16062A054F506A;1B1A1906055562464066;1C35092D2C154A5E6B6C;1D12111B0E1E17483C3E;1E2E20321C016869655F;1F5354495C60;200A0407210D230810015B63564B3D80;21360B04130324081F014C4D4E453C423D;2222342F5758646151523F44;2302033343413E;24020C072829312B055D59;2514473C5A;26120927182526300F1D16062A054F506A;271A1906055562464066;282D2C154A5E6B6C76;2912111B0E1E17483C3E;2A0A042E20321C016869655F;2B360B045354495C6760;2C07210D2308105B63564B3F3D77;2D00130324081F014C4D4E453C423D;2E000C22342F57586461515244;2F00023343413E;3035090002072829312B05755D59;310014473C676D5A;3227182526300F1D16062A054F506A67;331A1906055562464066;340A042D2C154A5E6B6C;35360B0412111B0E1E17483C6F3E;362E20321C6869653F5F;375354495C6760;380C07210D230810015B6356704B3D677774;391324081F014C4D4E45703C423D;3A350922342F57586461515244;3B023343413E;",
        ";000A00220362463C44;010B00072128291D334F50645D;02360002230605534855423F59;03000212300F24060568695A;0400042E27342A495C403C8C;050C04184A5E6B3E66788D76;06091A1B2B15014C4D4E;07352D321C14175B636151526577;0811130E16080147546C433C6A3D5F;0920070D190801563D60;0A0A032C2F104541;0B0B252631031E1F57584B3E;0C362203056246717B3C3F6D44;0D072128291D334F50645D;0E020423065348554259;0F00020C0412300F240668696E5A;1009002E12342A495C403C;113500184A5E6B3E66;12001A1B2B15014C4D4E;13002D321C14175B63615152656D77;140A11130E0316080147546C433C6F6A3D5F;150B20070D03190801563D60;1636032C2F104541733F;17252631031E1F5758727B4B3E;1804220362463C44;190C04072128291D334F50645D;1A09022306055348554259;1B3502120D0F24060568695A;1C2E27342A495C403C;1D184A5E6B3E66;1E0A381A1B2B15014C4D4E;1F0B2D321C14175B63615152657F;20363711130E0316080147546C433C6A3F3D5F;2120070D03190801563D60;2204032C2F104541;230C042526311E1F57584B3E;2409220562463C44;2535072128291D334F50645D;26022306055348554259;270212300F24060568695A;280A2E27342A495C403C6F;290B184A5E6B3E66;2A361A1B2B15014C4D4E3F81;2B2D321C14175B6361515265678074;2C0411130E03160847546C433C6A3D5F;2D000C0420070D190801566E3D60;2E09002C2F104541;2F35002526311E1F57584B3E;300022056246703C44;3100072128291D334F50645D676D;320A02230605534855426759;330B02120D0F2406056869755A;34362E27342A495C403C3F;35184A5E6B3E6676;36041A1B2B154C4D4E81;370C042D321C14175B6361515265677774;380911130E16080147546C433C6A3D675F;393520070D190801563D60;3A2C2F104541;3B2526311E1F5758704B3E87;",
        ";00001D2F10575868694F503C;0100122B1F495C5564;0209000207222829140605655D44;03000216063305474C4D4E51526A4B3F;04000C042E300F193C6159;0504182C43403E5A;06271A1E2A014A5E6B6C5B6342;070B2D1B1366;080A112526321C0815013C3D;0920032308170153546246413D;0A07210D310324565F;0B0E033448453E60;0C091D2F1005575868694F50717B3C6D;0D122B1F495C553F;0E020C04072228291406655D44;0F000204160633474C4D4E51526A4B;10002E300F193C6159;110B00182C43403E5A;120A00271A1E2A014A5E6B6C5B6342;13002D1B13036D66;14112526321C030815013C6F3D;1520032308170153546246413D;160907210D31032456735F;170E344845727B3F3E60;180C041D2F10575868694F503C;1904122B1F495C5564;1A0207222829140605655D44;1B0B0216063305474C4D4E51526A4B;1C0A2E300F193C6159;1D182C43403E5A;1E38271A1E2A014A5E6B6C5B6342;1F2D1B130366;2009112526321C030815013C3D;21202308170153546246413F3D;220C0407210D3103565F;23040E3448453E60;241D2F1005575868694F503C;250B122B1F495C5564;260A0207222829140605655D44;270216063305474C4D4E51526A4B;282E300F193C6F616E59;29182C43403E5A;2A09271A1E2A014A5E6B6C5B63427988;2B372D1B133F6766;2C0C04112526321C0308153C3D;2D0004202308170153546246413D;2E0007210D3124565F;2F0B000E3448453E60;300A001D2F1005575868694F50703C89;3100122B1F495C5564676D;320207222829140605655D6744;330216063305474C4D4E7551526A4B;34092E300F193C6159;35182C43403F3E5A;360904271A1E2A4A5E6B6C5B634278;37042D1B136766;38112526321C0815013C3D67;390B202308170153546246413D;3A0A07210D3124566E5F;3B0E03344845703E60;",
        ";003509001E2F554C4D4E453C51525D5F;010057586C646160;0200020E06100543;0300020721282923061F0565;0400042E2224533C7344;05360B04182526300F34335B633F3E74;060A1A13016246404B59;070C2D2B4A5E6B5A;0827111B0314082A0148413C3D;0920321C310316080148413C3D;0A35090319154754495C42;0B12070D1D2C174F50563E;0C1E2F05554C4D4E45717B3C51525D6D5F;0D57586C646160;0E02040E061043;0F360B0002040721282923061F653F;100A002E2224533C44;11000C182526300F34335B633E;12001A1303016246404B59;13002D032B4A5E6B6D5A;14350927111B0314082A0148413C6F3D;1520321C310316080168696A3D66;1619154754495C426E;1712070D1D2C174F5056727B3E;18041E2F554C4D4E453C51525D5F;19360B0457586C64613F60;1A0A020E06100543;1B020C0721282923061F0565;1C2E2224533C44;1D182526300F34335B633E;1E3509381A1303016246404B59;1F2D032B4A5E6B5A;2027111B14082A0148413C3D;2120321C3116080168696A3D66;22040319154754495C42;23360B0412070D1D2C174F50563F3E;240A1E2F05554C4D4E453C51525D5F;250C57586C646160;26020E06100543;27020721282923061F0565;2835092E2224533C6F44;29182526300F34335B633E;2A1A13016246404B5982;2B2D2B4A5E6B675A76;2C0427111B0314082A48413C3D;2D360B000420321C3116080168696A3F3D66;2E0A0019154754495C42;2F000C12070D1D2C174F50563E;30001E2F05554C4D4E45703C51525D5F;310057586C6461676D608E;323509020E0610054367;33020721282923061F057565;342E2224533C6E44;35182526300F34335B633E7974;3637041A13036246404B5982;37360B042D2B4A5E6B3F675A76;380A27111B14082A0148413C3D67;390C20321C3116080168696A3D66;3A0319154754495C42;3B12070D1D2C174F5056703E;",
        ";0000302007210D341556;01000217455D;020A0025262B2F060557586C5F;030B001406056246603C8F;0436000207282916105B6364656A;0537130E191F47483E;0622300F2C0168693F44;07021E33495C40413C;08090C04184A5E423D59;093504121A1B0308014C4D4E51524B3D5A;0A02272D321C1D232A4F507E61;0B1124535455433E66;0C0A2E2007210D341505566D;0D0B0217455D;0E3625262B2F0657586C;0F00140662463C4260;10000207282916105B6364656A3F79;1100130E191F47483E;1209350C0422300F032C01686944;1335000204031E33495C40413C6D;1418310308014A5E6B3D59;15121A1B0308014C4D4E51524B3D5A;160A02272D321C1D232A4F507E61;170B1124535455433C6F6E3E66;18362E2007210D341556;190217455D;1A25262B060557586C3F5F;1B14060562463C4260;1C09020C0407282916105B6364656A;1D3504130E03191F47483E;1E22300F032C01686944;1F02031E495C40413C;200A183108014A5E6B3D59;210B121A1B08014C4D4E51524B3D5A;223602272D321C1D232A4F507E61;231124535455433C3E66;242E2007210D34150556717C3F;25021745735D;26090C0425262B2F060557586C5F;27350414060562463C4260;280207282916105B6364656A74;29130E03191F47483E;2A0A22300F2C01686944;2B0B021E33495C40413C6F67;2C36381831034A5E6B3D59;2D00121A1B08014C4D4E51524B3D5A;2E0002272D321C1D232A4F507E613F;2F00112453545543727C3C3E66;3009000C042E2007210D34150556;313500020417455D676D;3225262B2F060557586C70675F;331406056246703C426084;340A0207282916105B6364656A;350B130E191F47486E3E;363622300F032C7544;37021E33495C40413C67;38183108014A5E6B3F3D675976;39121A1B08014C4D4E51524B3D5A;3A09020C04272D321C1D232A4F507E61;3B35041124535455433C3E66;",
        ";000A002E27202C2A475462464B;010B0002070D1E5666;02002F06150548456E5D;0300061705575868695B633C;040002130323495C645F;0507212829249060;0609341001534C4D4E415152;070212300F31031F3C61423F;080418220E032B080143403D44;090C041A1D14080833014A5E6B6C4F503D;0A0A022D1B16556A59;0B0B112526321C193C653E5A;0C2E27202C2A05475462464B6D;0D02070D1E5666;0E2F061548455D;0F000617575868695B633C85;10090002371323495C645F;11000721282903243F3E60;12000403341001534C4D4E415152;1300020C0412300F31031F3C61426D;140A18220E032B080143403D44;150B1A1D140833014A5E6B6C4F503D;16022D1B16556A59;17112526321C193C6F653E5A;182E27202C2A475462464B;1902070D1E5666;1A092F06150548455D;1B061705575868695B633C3F79;1C0204130323495C645F;1D0C040721282903243E60;1E0A03341001534C4D4E415152;1F0B0227300F311F3C6142;2018220E2B080143406E3D44;211A1D140833014A5E6B6C4F503D;22022D1B16556A59;23112526321C193C653E5A;24092E27202C2A0547546246717C4B;2502070D1E56733F66;26042F06150548455D;270C04061705575868695B633C;280A02130323495C645F;290B07212829243E60;2A341001534C4D4E415152;2B0212300F311F3C6F614267;2C3818220E032B0843403D44;2D001A1D140833014A5E6B5B4F503D78;2E0900022D1B16556A59;2F00112526321C19727C3C653F3E5A;3000042E27202C2A05475462464B;3100020C04070D1E56676D66;320A2F0615054845705D67;330B061705575868695B63703C74;34021323495C645F;3507212829243E60;36033410534C4D4E41755152;370212300F311F3C614267;380918220E2B080143403D6744;391A1D140833014A5E6B6C4F503F3D76;3A02042D1B16556A59;3B0C04112526321C193C653E5A;",
        ";00002E20391C246869655D59;010002345354495C5A;023509002707210D062A055B6356515277;0300132B06054C4D4E453C66;04000203142F1557586473614B3F;0512161743416A3E;060C072829310319015F;07360B02032C476C3C6E60;080A04182526300F1D1E0810014F503D;09041A081F01556246403D;0A022D224A5E6B4486;0B111B0E2333483C423E;0C35092E20321C24056869655D6D59;0D02345354495C5A;0E2707210D062A5B635651523F77;0F00132B064C4D4E453C66;1000020C03142F15575864614B;11360B001203161743416A3E;120A0004072829310319015F;13000204032C476C3C6D60;14182526300F1D1E0810014F503D;151A081F01556246403D;163509022D224A5E6B44;17111B0E2333483C6F423E;182E20321C246869655D3F59;1902345354495C5A;1A0C2707210D062A055B635651527F;1B360B3713032B06054C4D4E453C66;1C0A020403142F15575864614B;1D041203161743416A3E;1E0728293119015F;1F022C476C3C60;203509182526300F1D1E08104F503D;211A081F01556246403D;22022D224A5E6B3F447891;23111B0E2333483C423E;240C2E20321C24056869717C655D59;25360B021C5354495C6E5A;260A042707210D062A055B6356515280;270413032B06054C4D4E453C66;2802142F15575864614B;2912161743416A3E;2A35090728293119015F;2B022C476C3C6F6760;2C38182526300F1D1E08104F503F3D;2D001A081F01556246403D;2E0002092D224A5E6B4476;2F360B00111B0E233348727C3C423E;300A00042E20321C24056869655D59;31000204345354495C676D5A;322707210D062A055B6356705152677774;33132B06054C4D4E45703C66;34350902142F15575864614B;3512161743416A3E;36072829310319753F5F;37022C476C3C6760;380C182526300F1D1E0810014F503D67;39360B1A081F01556246403D;3A0A02042D224A5E6B44;3B04111B0E2333483C423E;",
        ";00090038041A221B194C4D4E44;0135000C042D321C2C335B6361655D77;02002E11130E1E06054754433C59;03001220070D0605565A;0400272F2A454142;050B252631032357583E66;06360A0324150162463C;07072128291D34174F50644B;080208015348553F3D5F;0902300F2B080168693D60;0A09041410495C403C6F;0B35090418161F4A5E6B6C5152403E;0C1A221B19054C4D4E6D44;0D2D321C2C335B6361655D77;0E2E11130E1E064754433C6E59;0F0B351220070D0306565A;10360A0027032F2A454142;1100252631032357583E66;12000324150162463C3F;1300072128291D34174F50644B6D;1409020408015348553D5F;1535020C04300F2B080168693D60;161410495C403C;1718161F4A5E6B6C51526A3E;181A221B194C4D4E4481;190B0A2E11130E031E06054754433C59;1A360A2E11130E031E06054754433C59;1B1220070D030605565A;1C27032F2A454173423F;1D252631032357583E66;1E090424150162463C;1F350C04072128291D34174F50644B;200208015348553D5F;2102300F2B080168693D60;221410495C403C92;230B18161F4A5E6B6C51526A3E7893;24360A1A221B19054C4D4E44;252D321C2C335B6361655D7F;26372E11130E031E06054754433C3F59;271220070D030605565A;280904272F2A454142;29350C042526312357583E66;2A2415016246703C;2B072128291D34174F50644B67;2C02085348556E3D5F;2D090002300F2B080168693D60;2E360A001410495C403C;2F0018161F4A5E6B6C51526A3E;30001A221B19054C4D4E717D3F4481;31002D321C2C335B6361655D676D8074;3209042E11130E1E06054754433C6F6759;33350C042720070D0605565A;34272F2A454142;35252631235758703E6687;36241562463C;370B072128291D34174F50644B67;38360A023A015348553D675F;3902300F2B08016869753D60;3A1410495C403C3F;3B18161F4A5E6B6C727D51526A3E76;",
        ";0000380C041A23104A5E6B5B63;010004122D1B13241F838A;020A002E11252622321C3406053C5D44;030B00200306330553544641;040007210D312B5659;050E031448453E5A;060E1D162F2A01575868694F503C6A;0719495C556466;0809020728292C081501515242653D;09021E081701474C4D4E3F3D;0A0C04300F3C6F614B5F;0B041843403E60;0C0A1A2310054A5E6B5B636D;0D0B122D1B1303241F838A94;0E2E11252622321C34063C5D44;0F002003063353546C624641;100007210D31032B5659;11000E031448453E5A;120900271D162F2A01575868694F503C6A;130019495C55643F6D66;14020C040728292C081501515242653D;1502041E081701474C4D4E3D;160A300F3C614B5F;170B1843403E60;181A23104A456B5B6378;19122D1B1303241F9583;1A2E11252622321C033406053C5D44;1B200306330553546C6246416E;1C0907210D31032B567359;1D0E1448453F3E5A;1E0C04271D163B2A01575868694F503C6A;1F0419495C556466;200A020728292C081501515242653D;210B021E081701474C4D4E3D;22300F3C614B5F;231843403E60;241A2310054A5E425B63;25122D1B1303241F;26092E11252622321C033406053C5D44;272006330553546C6246413F;280C0407210D312B5659;29040E1448453E5A;2A0A271D162F2A01575868694F50703C6A89;2B0B19495C55646766;2C020728292C0815515242653D;2D00021E081701474C4D4E3D;2E00300F3C614B5F;2F001843403E60;3009001A2310054A5E6B5B63717D7988;310037122D1B13241F3F676D;320C042E11252622321C3406053C6F5D6744;33042006330553546C624641;340A07210D312B5659;350B0E03144845703E5A;36271D162F2A575868694F503C6A;3719495C55646766;38020728292C081501515242653D67;39021E081701474C4D4E756E3D;3A09300F3C614B5F;3B184340727D3F3E60;",
        ";000A003837041A1316624640425D6A5F;01360B00042D194A5E6B4B60;020009111B032C06100548413C;030020321C310310061F056869;0400224754495C7344;05070D1D334F505651523F3E;063509232F01554C4D4E453C59;070C24575864615A;0802270E34082A01433D;09020721282908016E653D66;0A0A042B15536C3C6F;0B360B0412182526300F14175B633E;0C1A13031605624640425D6A6D5F;0D2D03194A5E6B4B60;0E2E111B33061048413C;0F0020321C31031E061F68693F;1035090022034754495C44;11000C070D1D334F505651523E;1200232F01554C4D4E453C59;130024575864616D5A;140A0204270E0F082A01433D;15360B0204072128290801653D66;162B15536C3C;17121825260D0F14175B633E;181A1316624640425D6A5F82;192D03194A5E6B4B3F60;1A35092E111B032C061048413C;1B0C20321C31031E061F056869;1C224754495C44;1D07121D334F505651523E;1E0A04232F01554C4D4E453C59;1F360B0424575864615A;2002270E34082A01433D;2102072128290801653D66;222B15536C3C;2312182526300F14175B633F3E;2435091A13031605624640425D6A5F;250C2D03194A5E6B4B60;262E111B2C06100548413C;2720321C311E061F056869;280A04224746495C44;29360B04070D1D334F505651523E;2A232F01554C4D4E45703C59;2B2457586461675A96;2C02270E34082A433D;2D0002072128290801653F3D66;2E3509002B15536C3C;2F000C12182526300F14175B633E;30001A1316624640717D425D6A5F82;31002D194A5E6B4B676D6076;320A042E111B2C06100548413C6F67;33360B0420321C311E061F0568696E;3422034754495C44;35070D1D334F50567051523E;36232F554C4D4E453C59;3724575864613F675A;38350902270E34082A01433D67;39020C07212829080175653D66;3A2B15536C3C;3B12182526300F14175B63727D3E7974;"
    };

    God God::from_index(const int index) {
        return God(index);
    }

    God God::from_name(const string &name) {
        return God(name);
    }

    God God::next(const int n) const {
        return from_index(next_index(n));
    }

    Luck God::get_luck() const {
        return Luck::from_index(index < 60 ? 0 : 1);
    }

    vector<God> God::get_day_gods(const SixtyCycle &month, const SixtyCycle &day) {
        auto l = vector<God>();
        const string s = DAY_GODS[month.get_earth_branch().next(-2).get_index()];
        char buffer[3];
        snprintf(buffer, sizeof(buffer), "%02X", day.get_index());
        const regex re(";" + string(buffer) + "(.[^;]*)");
        if (smatch match; regex_search(s, match, re)) {
            const string data= match[1];
            for (unsigned long i = 0, j = data.size(); i < j; i += 2) {
                l.push_back(from_index(stoi(data.substr(i, 2), nullptr, 16)));
            }
        }
        return l;
    }

    const vector<string> Land::NAMES = {
        "玄天", "朱天", "苍天", "阳天", "钧天", "幽天", "颢天", "变天", "炎天"
    };

    Land Land::from_index(const int index) {
        return Land(index);
    }

    Land Land::from_name(const string &name) {
        return Land(name);
    }

    Land Land::next(const int n) const {
        return from_index(next_index(n));
    }

    Direction Land::get_direction() const {
        return Direction::from_index(index);
    }

    const vector<string> Luck::NAMES = {
        "吉", "凶"
    };

    Luck Luck::from_index(const int index) {
        return Luck(index);
    }

    Luck Luck::from_name(const string &name) {
        return Luck(name);
    }

    Luck Luck::next(const int n) const {
        return from_index(next_index(n));
    }

    const vector<string> Phase::NAMES = {
        "新月", "蛾眉月", "上弦月", "盈凸月", "满月", "亏凸月", "下弦月", "残月"
    };

    Phase Phase::from_index(const int lunar_year, const int lunar_month, const int index) {
        return Phase(lunar_year, lunar_month, index);
    }

    Phase Phase::from_name(const int lunar_year, const int lunar_month, const string &name) {
        return Phase(lunar_year, lunar_month, name);
    }

    Phase Phase::next(const int n) const {
        const int size = get_size();
        int i = index + n;
        if (i < 0) {
            i -= size;
        }
        i /= size;
        LunarMonth m = LunarMonth::from_ym(lunar_year, lunar_month);
        if (i != 0) {
            m = m.next(i);
        }
        return from_index(m.get_year(), m.get_month(), next_index(n));
    }

    SolarTime Phase::get_start_solar_time() const {
        const int n = static_cast<int>(floor((lunar_year - 2000) * 365.2422 / 29.53058886));
        int i = 0;
        const SolarDay d = LunarDay::from_ymd(lunar_year, lunar_month, 1).get_solar_day();
        const double jd = JulianDay::J2000 + ShouXingUtil::ONE_THIRD;
        while (true) {
            if (const double t = ShouXingUtil::msa_lon_t((n + i) * ShouXingUtil::PI_2) * 36525; !JulianDay::from_julian_day(jd + t - ShouXingUtil::dt_t(t)).get_solar_day().is_before(d)) {
                break;
            }
            i++;
        }
        constexpr int r[] = {0, 90, 180, 270};
        const int deg = r[index / 2];
        const double t = ShouXingUtil::msa_lon_t((n + i + deg / 360.0) * ShouXingUtil::PI_2) * 36525;
        return JulianDay::from_julian_day(jd + t - ShouXingUtil::dt_t(t)).get_solar_time();
    }

    SolarTime Phase::get_solar_time() const {
        const SolarTime t = get_start_solar_time();
        return index % 2 == 1 ? t.next(1) : t;
    }

    SolarDay Phase::get_solar_day() const {
        const SolarDay d = get_start_solar_time().get_solar_day();
        return index % 2 == 1 ? d.next(1) : d;
    }

    int PhaseDay::get_day_index() const {
        return day_index;
    }

    string PhaseDay::get_name() const {
        return phase.get_name();
    }

    Phase PhaseDay::get_phase() const {
        return phase;
    }

    string PhaseDay::to_string() const {
        return phase.to_string() + "第" + std::to_string(day_index + 1) + "天";
    }

    const vector<string> Sixty::NAMES = {
        "上元", "中元", "下元"
    };

    Sixty Sixty::from_index(const int index) {
        return Sixty(index);
    }

    Sixty Sixty::from_name(const string &name) {
        return Sixty(name);
    }

    Sixty Sixty::next(const int n) const {
        return from_index(next_index(n));
    }

    const vector<string> Sound::NAMES = {
        "海中金", "炉中火", "大林木", "路旁土", "剑锋金", "山头火", "涧下水", "城头土", "白蜡金", "杨柳木", "泉中水", "屋上土", "霹雳火", "松柏木", "长流水", "沙中金", "山下火", "平地木", "壁上土", "金箔金", "覆灯火", "天河水", "大驿土", "钗钏金", "桑柘木", "大溪水", "沙中土", "天上火", "石榴木", "大海水"
    };

    Sound Sound::from_index(const int index) {
        return Sound(index);
    }

    Sound Sound::from_name(const string &name) {
        return Sound(name);
    }

    Sound Sound::next(const int n) const {
        return from_index(next_index(n));
    }

    const vector<string> Taboo::NAMES = {
        "祭祀", "祈福", "求嗣", "开光", "塑绘", "齐醮", "斋醮", "沐浴", "酬神", "造庙", "祀灶", "焚香", "谢土", "出火", "雕刻", "嫁娶", "订婚", "纳采", "问名", "纳婿", "归宁", "安床", "合帐", "冠笄", "订盟", "进人口", "裁衣", "挽面", "开容", "修坟", "启钻", "破土", "安葬", "立碑", "成服", "除服", "开生坟", "合寿木", "入殓", "移柩", "普渡", "入宅", "安香", "安门", "修造", "起基", "动土", "上梁", "竖柱", "开井开池", "作陂放水", "拆卸", "破屋", "坏垣", "补垣", "伐木做梁", "作灶", "解除", "开柱眼", "穿屏扇架", "盖屋合脊", "开厕", "造仓", "塞穴", "平治道涂", "造桥", "作厕", "筑堤", "开池", "伐木", "开渠", "掘井", "扫舍", "放水", "造屋", "合脊", "造畜稠", "修门", "定磉", "作梁", "修饰垣墙", "架马", "开市", "挂匾", "纳财", "求财", "开仓", "买车", "置产", "雇佣", "出货财", "安机械", "造车器", "经络", "酝酿", "作染", "鼓铸", "造船", "割蜜", "栽种", "取渔", "结网", "牧养", "安碓磑", "习艺", "入学", "理发", "探病", "见贵", "乘船", "渡水", "针灸", "出行", "移徙", "分居", "剃头", "整手足甲", "纳畜", "捕捉", "畋猎", "教牛马", "会亲友", "赴任", "求医", "治病", "词讼", "起基动土", "破屋坏垣", "盖屋", "造仓库", "交易", "立券", "安机", "会友", "求医疗病", "诸事不宜", "馀事勿取", "行丧", "断蚁", "归岫"
    };

    const vector<string> Taboo::DAY_TABOO = {
        "8219000776262322200C1E1D,06292C2E1F;0F11185C0001092A0D7014692982837B7C2C2E302F802D2B,06454F2089;111852828370795B302F404533802D152B39201E23221D212726,0F2E1F010D29;004023222088,0F29111847;11180001032A0D70795B2C2E302F802D4E152B33714161201F26,52095847;0F17000102061979454F3A15477677,241F8920;34357C88,7129;1551000403706A454F3A3D771F262322271E1D21,382B415220;0F000102037039297175261F1D21,454F2E156341;00076A54196348767765,792029711552890D382B;11180001020439332C2E302F2B5844477515634C1F2721,0F520D19267A29717020;297170192C2E2D2F2B3E363F4C,0F52156320010347;4C78,297172380D2A2E0F474841;18115C0001702A2C2E2F5282837129795B6375802D154C,1F208924;1811795B032C2E302F802D4163754C27261E1D2120,010D0F29521F;00401D232288,71290F4720;0F170001020E032A70692C2E302F802D2B0D7129474C201F2322,5211183809615D;0F1811000102062A0D2C2D804B2B672E2F7129,70471F8920;0007343588,0F71296B7080;175447440D15828377656A49,2B2E1F892022;11187129705B79000106032A0D397B6F7C802D2C2B61756627261E0C1D21,0F2E154147;0007385476771548,52061F20;0106111839513A2C2E2D2F8B804B4723221F63,71522920;1118000717161A2C2E3371292B56433D6375363F,0F0103472089;161A7888,292E1F0F3861;11180F00012A0D70795D7B7C39332D2C2E4E4863664C,064F478920;5452828379195D00012A0D7B7C2C2E3348156366242526201E,0F7129;00262788,292C2E1F2B2F;040318111A17332C15290D200C7A,47450638;0004031A170F11332C2E302F1571292A657677451949,70201D52;007B343588,87;00010670175B71292A152322271E,03637C2B38;04067033392C7161262322271E1D210C,;000715547776,521F;181100012C2E2F1F,0F38;70076A363F,2920;7888,292E1F;0F707B7C00012F75,5220;528303395B2F1E20,0F01;4088,87;02060418110D332C2E415B637566262322271F20,520F;0F181100012C2E7129,5220;7C343588,87;0001020603691817452C2E2D498244,412B6A096338;393588,87;076A48,45752F29384C0F204F612B;000301394F2E154763751F27,0F707A802629710D1920;4F2C2E2B383F443D433663,0F0147892015;201E27262322,88;0F000102700D335282835329711563,3804897D4520;6A0339332C20528283531563,29713801000F0C47806B;005088,87;291503000D332E53261F2075,0F5238584F45;003988,87;3435000788,87;150001021745512E443D65262322,2B63387C;394888,87;00036A7415384878,45751F20240F522E824F;00010203332C2E2F1558631F,0F1920707A29712646;0717363F1A2C4F3A67433D8A,71290F010347;",
        "0007010618111A332D302F15262322271E530270164C,560F7129;003988,87;073918111A17332C2E71292322271F1E20481D45548283,38002F70;700F181126151E20001A7919,;5040262788,0F712903;7911192C2E302F00030401060F1571292A75,707C2052;0079701811072C2E01060F33152627200C7A1A302F4576631F2B,80523829;39343588,87;040370181123220F1326271E2021,2915;262322271E202188,1F45;0001060403232226380F767754,56802015;0070071A010618110F5B52836775,632620;00010607155B5C26271E2021165D82,38470F29;3948007888,;528283530339454F0D297115332E2F637520,0F007058;5282835444360F11756415,2C2F29016B472E2B2038;0039504088,;0F0001022E792D3E75663D19,472063703852292B;0F000102032971152C2E19,4720637038522B;343588,87;0F52828303700D332C29712E1F27201E2322,15450175;00261F23221E201D2188,;003988,87;52828354754C2971150301022E,0F63206A0938268941;151A82832627202322,580F7003632E1F297C;00394C786F88,0F2E4420;0704031118528283542D2E4E49201F1E1D2127,292B000C;0F706A151E201D528283544466,47010C2E292F2C38;394088,71294709636F7C44;0F0003450D3329712C2E2F1575,528963705A20587D7C;0F111829711500010370390D332E750C201F,4552822F382B80;0034353988,522E1F;0F1118032A0D545282831A802D2C2E2B71296366774744201F26232221,010900150C;0006261F1E201D212322,0F29381118;0006547677,0F5229151F20;111800010206071979697C67474475664C,0F16298920;000102071282542627201D210C4C78,29580F2E6352031F;00784C793988,0F29702E1F2089;0F03390D332C1929711563261D2E2322,382000521118750C706B;702D155482830F63262720,53292F017D4F38442B2E1F47;4088,030F565A61206B;0F181179005B712980152D4E2A0D533358,52702089;0776776A742623221F200C211D1E,11180F2F5206802B;00343588,060F52;07565A5282835463756677261F20,010F152961;0007363F8A3988,09292C20890F;0F11181200171A7919547638,5215201D;181179000607040D03302F5282834F3A45512B1533664C47,090F702E2089;828354151A4C200C1E23221D212726,030F522E1F;0039787988,1F2E20;111871545282832979397B7C69152B2A0D33485324251F1D1E26,6B00702F800C20;0F18110001027939706954528283685D15565A75201E1D26,29032E;00170F79191A6540,712909387C20;00676588,0F20;0F00071A706A717677492923221E202726,80522E1F;343588,0F5220;111800020D041A796933483E5347446563751F1D212026,010F09150C;262322271E201D21,52450F4F;0038262388,5215;040307177938494C,0F262070;",
        "0F00030102705282832E544779,2920454F754C38;00010275261E0C2322,6303706F0F292E1F;033945302F828375262720,297071000F2E1F38;000102030F7039453319152E2D2F63751F0C1E20,71290D3847;7917155B0001025D,0F522E3820;38394088,0001202B;0F00175058,5D6B80382E;110F0001702C2E7129201F,5206;0007396A48343588,0F20;111800012A0D2C705271292E201F,15386179;3F656477,0F2B712920;11000170792C2E7129,0F52201F;110F00017052792E1F1E,71290D2B20;0001020626232227201E,0F2E03801F;1179302F832627201E,0071292E1F;0001067052832E71291F20,030F384775;79026A17657603,522E201F;004088,0F014720;010206110F452C2E7129095B5226232227201F0C,58804B036B2B38;69687011180F791966762627201E,0352292E80;00077B7C4834353988,295220;00170F332C2E2D2F802952443F26232227201F,15637C38;006526232227201F,88;0403010218111A17332C2E2D2B15713E6575,4538206429;0007030401021811171A0F2E2322271F1E706749528382,202F2938;000102081A158382262322270C1E,700F292E;1A162623227954,0001710F29;00061A161718110F292A0C26271F21797001022F49,47;1516291211020056,063820;3840,0001202B88;0403080618111A16332E2F152A09537919702C5445490D75072B,80632038;0001081811171A160F1571292A26271E20396476452B0D,632E5238;7B34,87;010206040318110F2E292A27200C70072C302F541F392B49,3815;64262322271F2021,0F2F2938;0002070818111A16175B153E445D5452838265647576,2038454F;000701020618111A1752838254230C7027,26203829;000102261E2027,03476F700F2971382E;15391A302F82835475662627201E,0F702E46290047;0F150370002E0D3979528283532971331F1E20,477D;0F0302791566046F,29710D722A38528283202E45;383940,6370018975202B454F66;3907,87;0F000170390D332E2971152F63751F1E20,52836A38;00397C343548,88;000102030D70332C2E29712F534426201F1E,0F3815;6526232227201F,87;7100030170391959152E2D2F2B,0F201F4F75668938;0F030102392E15634447001F1E,293845200D7075;00161A5D454F153826201E27,7D0D29;1A454F548283,87;0F00010203700D332E2F1929711552828353261F201E2322,;0F171170792F5B1566770001032C2B802D,29387C2071;50400088,87;5C11180001027170520D2983822B15200C,03802E3863;2E260F27201F,523815292F1A;7B7C343588,520F;00060724232227261F2025,520F157929382F;003F651F0C2027232288,0F29;00076A386563,0F7D892066454F52754C;",
        "00077663,0F29713820;000304080618110F1A2E2D0D3371292A2C302F7566010239454E802B,6320;181117332C2E1526232227201F1E3E,38030F5229;0103040818111A155283262322271E20217A79708230,38472E63;00483F,6338200F;03041A174533302F56795B3E808239528354,700F2920;17262322274050,80387C6B;000F01111A1615292A2627200C2C670279538283543E49,6345;00010618111A16332C2E2F2D27200C07483A450D,15528338;34357B7C,87;002E2F18110F5B3315292A26271F20210C7A70710102393E19,035A;000304111A33152D2E302F71292A5283530770022B,0F634520;1A16170F13152654,3852204F;0018112C2E01040607332D292A09270C2322696870302F47023945,38205280;18111A16175B3315262322271F1E201D215D828354433E363F754551,00030F29;00700F1715262720,472E3863;3F87,2B38200F;030402111A16175B4F3A2B153E0079015D54528382696A51,7006200F;000F1320,63803829;0079181A165B332F2B262322271E2021030469702D4E49712930835D,454F;00030401061A16170F332E71292627200C02696A45514F0D2C2D4E497A,2B;007C343588,87;0F00701783821952712C2E1526271F,03380620;52838253000103297115332E2F19,0F89514F6A66207545;6A170F19,5845754C201F4F3824;0F000301020D297115332E1F0C,16522026;1545332C2E2F83826375662620,0F0003700D71292B;000102060F17705282797823221E2027,2E7129;3F74397677658887,0F384720;5452838203152F802C2D,2E1F20897A700F29710C7D;00010F17505840,565A803852828363;0F00030102700D19297115332C2B535448,2E452089;0F03000102700D29713963451F0C20,528238542F158061;34357B7C88,030F;118283155B20272E1F21,0F0338;0001020607036A5D397C2163664744,0F4E252089;5482836376656419786A,29803020;0F18110001702C2E71291F0D2B152F2127,52821620;1783822C2E5B26201F,0F010D29;00797083821754,0F2E472D4E1F;000739483F66,0F20892B;54528283036F796A153E65,712963;0F17795B54828358,52807C38;0F5C111800015B712952831F20,756A25;01067071292C2E1F20,1103150F52;343588,0F715229;0F170070792C2E261F,0403412322;03027011170D332D2C2E2F716152828354,010F201F;6A170F1963766F,5452201F;030102703945802D2C512B7129092322270C7566,112E5282;1A5D453A332C2E2F4B25262322271F201E1D21,000F7047;007983821A160F1719,632E20471D6B;483F88,87;040318111A16175B795452838215302F6563395D,38702920;000F1323222627,2E38290315;010203040618110F3315292A271D200C6339171A712C2E30491E21,7A;0039262322271E201D210C0748766465776A,150F3829;3435,87;007018111A1617192E15382627201F656477,4F09;00030418111617332E2D2F292A52835407020D302B,090F4520;",
        "528283530003010215392C20,1112180F29560D2E1F7545;004D64547588,0F29;2A0D11180F52838253037039156358332C2E,38200026;00702C2E164C157126271F1E202425363F,29386A032B;005088,032C2E1F;0F00010206030D7129302F79802D7C2B5C4744,11701D20528338;000403110F527079156523221E2027,0129802E1F6B;00384088,15296763;000102060775261F20,71290F7015;1100010206702D804E2B2620,0F52540D;0007397B7C343588,01065220;0776776564,000F293820;00010206111803302F565A802D4E2B871F261E0C,0D0F52;00763988,0F20;110F70528375660D7129,012E1F2026;0001020617385482,030F47202B6B;0039787088,2E1F89034F206B;0706397B7C794C636A48,520F71294720;02703918110F7919155282756626232227201E,012C2E1F0C;00384088,0F202E157C;5C0001020652825B0E03804B2D4E2B752024210C,292E565A;000103020611187B7C2D4E616439201E0C26,522E4744;000734357B7C3988,0F52822920;87,;0004031811171A5B332C2E155D52,0D292045;0088,090F15;18110F197982832E230C271F1E7A70525463,26202915;00011A1615262322271F1E200C214C,472B0F11;00190F153917701A48,472E1F2003;11037B7C2E2F7129,0F5220;007952151E20,0F2E1F;00384740,0F20;0006522E261F20,0F7129;0F11000170717B,522E1F;007B7C3988,87;076564,0F2920;,87;393588,87;0F03700D33195283825329711563,01260038206B;0F70161715232238828326271F20,7D0352;70504C7888,87;0001030239450D297115332C2E4C,0F54207052833863;110F03706A795215636626271E,0C012F38062C292B;0040395088,87;000103392E54827548,19700F58157A2038;00010203390D3329152C2B751E20,2E1F544753524582;0039343588,87;3F4888,87;000102033911170D3319152E2F0947442627201F,;393488,87;0F0102037039330D5283822971152E1F0C,0026206B;001A1715828344363F261F1E200C2322,0F476B520363;0070784888,0345201F;000102031118396375664819,1D413870208029;0370823F0F6A5215,010D582E1F202C2F29;00387765504088,0F157C;070039201F0C2788,06030F292F;003926271E20747677642322480C06,2E1F;00073934357B7C88,0F52;073F7765644888,0120;",
        "0F110001702E2F71291F20,06;110001527B7C2E75,0F20;0F11707129,2E1F20;1811002E1F8283,0F20;0F1A0070153871291F20,7A76;3F6588,87;0F1811700001062E2F1F20,7129;18117915384C,5220;07404826271F1E2088,87;0F00010203700D332E2F192971152B52828353631F20,;00037039041A26271F1E202322,0F2F2C335129452E0D3A;0039343588,87;0F0001020370332E2F0D19297115637566302B2C3979,;528283000103451915332C2E631F2720,29716A0D0F70;653988,87;0F00010203528283157033,752971206B452F2B262E;0F000102700D332C2E297115383F631F20,034756;394888,87;528283530370331929272E2B2F631F1D20,0F156B38;1979,3F2F2E45207D;074048261F202322,0F71454F15000180;0F000102030D70332E3919528283532971152B2F201F0C,;0001020339161745514F2C190F1A152E2D2F304979,;3435073988,87;11180F5C000102030D332C2E195329711563261F202322,5283;5282830001032E1570637566302F391F,0F47297120;39701117302F713819297566,004551152C2E201D1F;0001020370528283631575712D2E4E3E581F1E1D,292C2B45262080;0F82833D363F776424,15462F2C52032971;3F8A657788,0F2029702E7D;11180F0001020339700D29716375662E1F2620,38155680;03111A171538193E3F,0F632C2E70454F200C;110F1A6A702C2E1952828353712F6375,4520150001;5282835300010670802D2C2E4E155B201F1E232221,380F71296A;0F1118000102030D70332E2C192971153953631F0C262720,52836125;000739343588,0320;18110F3900010203700D3329711563752E1F0C201D,38525D;000102031811392E2D19528283543E4463751F20,152F1A290F;00657688,6B0F52;0001020311180F702E1F7952828368332D6749443E46630C1E1D21,292B20;0F1700707129385C363F3D1F1E232226,80412B202F;00398A7988,0F20;0F111800017C5C2C2E7129,5270153820;0F1118795B65170002195D,52382E8920;0007711F204840,010F291538;000106025B75712904032D302F382B2A0D801E20,2E1F0F;0F1118060300017B7C792E39767566261F20,71298051;000739343588,8920;074888,06520F38;5282835B79037B7C802D2C2E4E302F2B38493D4463664C1F2021,0F0D7129;63767788,522E0006206B;0F00010206181139702E1F686F6A792D2C304E153375664923221D21,52296B0D80;88,;3F8A6588,1F20;0370110F45510D3371290941614C522623222720,;1966583F6588,87;03700F,79192C2E2D715275262322271F201D21;0F11700001522E71291F20,2B;0F117B7C2C2E71291F20,5203;00343588,87;",
        "00343588,7129565A;00060403702C2E4C154947443D651F,0D29;528283530339332E152C2F58631F20,380D000F29;006588,29704720;0F1118175C000301027039450D29332C2E2F15631F,895820;0F161A17452F0D33712C2E2B5443633F,150170208903;70786288,06802E1F;0F0001020370390D332C1929712E157563548283534C,202489;5B000102073911522C302F3A678B363F33490D482425200C1E2322,0F15382E1F61;00076A74504088,5229702C7D;0F110001708371292E1F20,0338805156;111817000106702C2E71292A0D33802D302F4E2B44,0F522520;0007343588,290F71;0F5B8270000102060403161A494447,386A418920;11177B7C52832C2E5B1F20,060071292F0F;003888,52201F1D47;000102062A397129797B7C2E1F2425,162F5D2026;0F172C2E387129363F7566512D4E4461,0103475220;008254,06462F2E1F;0F181117795B5C007054292A0D690403332D2C2E66632B3D,89454F38;030270170F45513A2C71295282832A0D532D24252623222720,155A382E1F;00076A0F3874485040,06707C25;5B71297000010611182A0D39792C2E332D4E80151F202621,52454F38;00077665776488,52820F2089;34357B7C7788,0F29;0F705B0004037C5D15653F1F26,522B4738;181179190E332C2E2D52637566262322271F20,;0076645088,87;0F1100017B7C702E7129,522B;1A38712975,0F20;0026271E20,2F2E1F;18117001061579,712920;0F11707B7C5271291E20,2E1F;0F00074850,8920;0F1811705200012E71291F20,38;18117000012C2E7129,5220;87,;0F18110001261F20,0352;037B7C2E2F261F20,0F;006388,87;0F030001027039452971150D332C2F6327,20528283;020F11161A17454F2C2E2D302F2B38434C,20700163;003988,87;0F00010D0302703352828353297115632E,2089454F;03027039450D332C2F2D2971528283636626202322,5815;006A5040077448,702B2C0F2F29;0F00030102700D332E2C192971155382836375261F1E20,;0001020370450D332C2E2D152971,0F528289201D;343588,87;52828354443D65002C2E15495D1F,0F417D712B3863;528283546315332C2E2F26201F2322,0F0D45002971756B;003888,87;393588,87;2C2E2D2B156343364C,0F4729710D708920036A19;00788A88,0671292E;11180F000152548371702C2E2D4E303348492A156144474C63,89201F384506;0F0300017039712952542D2C302F80380D2A363F3349483E616320,1118150C1F2E;0F006A385040740717,1F7063;0F1118000102030D70332C2E192971158283535426201E2322,471F;77766564000788,0F52201E89;",
        "110001392E1F20,0F7129;00343588,87;0F1152702E2F71291F20,0001;0F1152702E2F71291F20,7A;00385476,521F;0F528300012E7129,0920;363F6526232227201E88,87;0F11700001397129,2E20;0F0001067C1F20,5229;0F705215261E20,012E1F;0F001A651707,565A58202E1F4763;297115030102195282830D332C2E,0F1F5863201D89;0039077426271F1E20,0F29713852822B63;343588,87;0F03706A4F0D332C528283532E29711563,450075;0F0370010239332E2C19528283532971156375262720,;003854637519,205D1D1F52151E21;0001020352666A,0F7020262938172F;00261F2322271E200C88,;007082624C,0F38202E7D4F45471F71;0F000102030D332C2E195282835329716375261E2322,;0F033915666A52261E272048,382E2F6329712C01;003988,87;00010203450D3329152C2E2F5375,0F63896A1D38;39006A26201F,0F520D38580629712B;343588,87;528283542E03700F111869565A7566631F1E2021,297138000C;0F1118000102030D70332C2E195282835329711563261F0C20,47457525;00173882546365756619,466115201F701D475224;0F18000102111A1703154F2C2E382D2F807566,716370891F207D;5D0007363F232227261E21,037C0F471F20;0F00701A17820E544C5C78,7129632E1F382089452F;2C2E5B000739337C38802D44484C2425201F1E272621,52297015;0F11185C0370332D152322528283636626271E,2F292C2E1F000106;000F7765,2E1F7C46;111879690001020370396A2E2D528283543E637566,0F380D582920;00013974150726271F1E200C,0F06520D297170382B45;34353988,0F20;0F528371295B795D2B155333565A446375661F201E272621,00016B0C41;0F181100010603797B7C802D302F2B6743441F202322,2952477D25;11180F71297000010604032A0D793969302F33802D636675,201F52565A1E;11180F000704030D7C684580302F153867534775,702041;00262322271F1E203F8A65,52290F0380;002C7080305C784C62,2E1F4720;000704036939487C4466,0F70112938;54528283700001020339482D301571565A363F637566,06292B201F89;005040,522E1F0F2C20;18110001032A0D835B7129302F791533536678,0F20891F1D;076A7626271F1E20,0D0F29382F2E;7B7C343588,0F70;11180F71297052828354792A0D33802D153853201F1E212627,012F564766;0001067011185B0D332C2E2D712909262322271F200C,0F526325;00195475667688,5229152E20;0004037B7C0F79494766754667,802938692089;003F657788,7152290F032B;525400045B17791A565D754C7866,2E1F207C;71297C790001062A0F802D,5215705D;0470170F191A134C8283662426232227201E,;00170F7665776488,;074888,87;",
        "0F0001020D700339332C192A82832971152E1F0C20262322,0652563861;1F2027260076232288,0F295282;34357C88,0111180F2920;0F030001022A0D3945297115528283637020,476A382E1F44;5B11180001020328700D332C2E195282837115632F751F2720,290F4766;0F0001021A175D2C19152E302F7182836379,8920704F754541;0F11180300706A2E1549466319,292F26806B382B207545;00704F0D332C2E2D15363F261F20274C,0F2906036F47;0F11180001027039302971542F7526201E,63472E151F58;390001022C2E302F1575804B2D261F20,0D0F0319707D5229717A;076A79040363660F5D363F,52292E1F20382F155601;006A38075040,0F630141202B454F;0F1118000106287129705B032C2E302F802D4E2B201F,5283583841;002876396577261F20,5282290F;07343588,0652;181100012A0D52832953411E20,2E1F0F4715;0F0001062871292E7C528283032C5C2A15767765,11185D89206B;0F181138171A7975665B52835415,47701F8920;0F181100062839707952542C2E302F03565A7566441F1E,0D29802B20;0F280001363F8A4326232220,2E1F47032F7D;0F17000728705448757A,522E1F15562F;00076A74173926271F1E20,0F7029522B;04170F79195D1A637566363F76,01522E8920;700718111A302F717566,0F2B2E20;11180F000128032A0D7129302C2E2F2D802B09411F1E20,52835438;0076777566262322271F201E,0F11185229;34357C88,8920;010670170F0E3A294152828354262322271F201E,2E181544;01023918112E2D493E52756624262322271F20,;04033918110F0D2C2E7129332D2B72528283547566,;017018110F1A2E15495247828363462322271F,;0F000106387129,2E1F;0F707500261E20,382E1F;181100012C2E2F1F20,0F52;181170792C2F7129,5220;07504088,0F01;0F0001062E7129,5220;7665261F20,0F29;077C343588,87;0F18117052000171291E20,2E1F;0F181100017B7C2E71291F20,036F;181100015B3875,2E20;0F000102702E15471F1E,294F2B452C2F2680;0F000102700D332C712E15261F201E,80036A614738;0001020370392F80712B546675201E26,1F58472E15;0039076A7426271F2048,0F79197029717A38;04031975363F6366,0F5401202C5282832E2F;3807504088,87;00020370454F0D3933192C2E2D156375261F202322,0F71;003F261F202788,87;343588,87;002627651E20232288,87;0F0D33000103452E528283297115752620,63386F70;0003391982835475,2E1F0F6A702971722A0D;0F00010203703915632719792322,8026204529715875;002E4344793F26271F20,03702C2F292B381A;001A2B5448701938754C,152E202425;0039332C2E2D2F152B4644261F1E,0F7019382971637A;11180370392A0D3329712C2F156375795B5D,450C8900382E1F2001;5040000738,0F7D7C584F012063452B;",
        "000150402627,0F292F2B;0079110F0304062A528323222627207A19701A2C2E2F5D82,2945;001779332D2322271E2007760304,38290F;0007343588,71297063;0004037039180F332D152952262322271F0C533A82,41178047;0079192E2F030417332D1552837A5D,4E20;001A170F1379232227761926,712938;87,26205282;001A170F5B332E2D7129261E203E5D,15035282;007022230726,2E17712952302F;00077A7088,87;87,;07262723221F40,0F712952;0F000102070D70332C2E19528283297115637526201E2322,;03392D2E332F211D201F1E27,0F7015380029710D1958;343588,87;0F0102700D332C2E2F0319528283531529716345261F2322,;5282835300031929150D332C2E63,0F21704520897175;006A79190F6F2627,6B4620453829;00211D1E232288,;0F7045332C2E71201F1D21,47011552295303;00704888,87;0F00040370396A742E15444948,4589384F20;5282835303702971150D2F,38896A6D0F20;0007504088,87;0F00010203700D332C2E1929711552828353637526202322,;393588,87;007C343588,87;0F11180003706A4F0D332C2E192971155363751F20262322,5247464161;528283545363000103332E15,0F1F197029710D757D20;0F006A1938271779,565A4575522F801F1E63;001D23221E2788,52290F2E1F20;0F175B3975660745514F2B4825201E211D,010352292E;007007482088,2E1F5847;0F110039702C2E522F1574487B7C2D4E804B,098920453861;111852828353546319297115030D332B2C,060F892E38201F;0007504088,0F291570;030102062C2E543E3D636679,380D1946297100;0339332C2E302B66201D1F27,0D2971010015520F6B;34357B7C88,7129;0F111800010203700D332C2E192971152F4B49471F270C2322,52562B20;0F111800010203391929710D1552828353,2075708945630941;00177688,0F52804F25;00396577647969271E2322,52012E1F262061;1707702C2E71291F20,0F52000106111D;0070,0F292C2E791F;0F18110001702C2E7129,6F454F098920;705282835B0D2F71,0F202E41;0007504088,060F71702F29;0F5C5B0001032A0D7052832C2E71291F20,1118517D46;07762623221F1E20,000F1552296B2F;88,6B;181100012A0D2C2E2F2B2D304E447129831F,0F0941613820;03020E0F18110D332C2E2D2F4971293E615244756653,892025;000F76,032E1F522C292B;0028397976771E232227,0F522E474420;7039170F45513A2C2E7129242526271F201D,0001035215;0001027007824878,2E3889201D;703911170E2C2E2D2F4B15712952633D,092B8920;03047039171A533852443D363F,;",
        "111879076A1A171523221E272024,5229700F1D012E2B0C2F;390050404C88,0F5282296920;261F1E20232288,52290058363F;0F0001020370332C2E2F1575261F,2971476A45825238;0007343588,0F292F7020;00021719792B155D5466774962,010611180F2920;0F1118528283530001035C702971152B332C2E63201F1E23222621,6B75452D4F80;00177179546A76,0F52443D1F;0001020603700F7B7C2E1F692D48302F565A586366240C21,2B151A2920;0F1A1716007015713F261F2720,5263587D2B4703;005C702C2F802B154C78,5A562E1F2089454663;00037039454F0D332971152C4C48,090F476341382E;11185282837975661271393D692D15565A201E262322,292F060D0C;004088,0F52;767788,5282002920;0F111800010206032A0D097170292D302F1575761320,521F4725;000739343588,520F;181179828354637566,0F52290120;5C0F1811790070528371291F20,2F03805125;003854767788,2E1F5220;0F18110001707B7C0D7129,52565A152B20;170007386A7448363F261F1E,030F79636F20;11180F000102587B7C5282837971302F804B2B497675,09612E1F20;705C4C39171A4F0E7971295B5248,0F2E1F1D;076A171552837982546578,712970010F;004C504088,0F521547;7665262322271F201E21,0F00298071;00010206090D5B7952828354685D7B7C443D77656366201F1E,030F47454F;343588,87;790F181113332C2E2D302F1554,70012038;00040301067018111A0F332C15292A261E200C7A7919712F5D52828354,5617454F;003826232277,632E2052;000106073018110F3329271E0C7A0D75,38262015;0F005B261F20,2E2F;384C,8920;076A696819,0F29;036F791E20,522E1F;00654C88,;262322271F1E20,7129;0F18117000012E71291F20,527A;0039343588,;1811795B5466,0120;0F1811705200012E71291F20,062B;003F88,87;000102035270392E2D5863,0F381D2B29212015;00391A6A15384C4943363F7448,0F0379472B63;00701A17794C0F302F715475,2E454F892024;000102037039714515750D33,201D381F092E0F11;5282835479036A2627201E,0F380D70297115012F;4C4088,87;261F201E232288,;002627241F1E20232288,;0039343588,87;0F0211195465756679,2F384570202B6A;0F0052037029710D332C15,7545584F89201D21;0F003854,20521D21;0F0001020370390D1952828353542971631F0C,1520;0F0001022E154826271F1E203874362322,0363;0001020370392F2971152B54754C,45891F0F2046;000370396A450D332F4B154C,0F20897D41381F2E;",
        "00790F072C2E0103047018111A262322271E7A302F5448637545,29381556;6A79363F65,0F292B71;000118111A332C2E2D1571292A23222627200C7A791970302F5D5282835456,387C454F;000118111A332C2E2D1571292A2627200C7A1979,387C;00040318110F1519262322271E2021,52821F38;0039343588,87;00390103040618111A17332C2E262322271E157A7071302F45631F2075,807C;000118111A16175B154C26271E200C232279302F5D528283547543,0F297C7A;074888,87;87,;010670181126271F202165,2938;000770171988,0F2E2038;000106040318111A170F33292A26276A201D0C7A71077C1F1E74694F,52;87,;5282835354037029711575262720,631F58000F2E3801;0F0001020370390D3319297115632E2C752620212322,;0339332C2E1575201E26,0F520D631F29712A724738;343588,87;0F00030D70332C2E3952828353542971156375,6B20;00010203396A79637566201D211E,29387D71707A;00076527262322,1552825A201D0F38;3988,;1500443626271F1E,29710F47380D195203;000788,;0F0370390D332C192E2971637547202322,5815;031A2B7915656A,0F177001204529710D632E2F;0F03700D332C2E2971152F52828363,01004547380C;0F000102030D7033528283534529711520,634758;006A6F391974,0F2E614447702C292F71201F3852;34357B7C88,0F20;11180F00010E715229702E79692C2D2B15093954444C66,2F565A8061;000102033945332C6375201D21,0F1929710D70;07487677393F88,0F2952151F1D;0F17000102060370392E52828353331F,452F2C266A79292B2038;161A0F1526271F4C,5861034738;3950177088,522E1F0F20;11180F0001020370391952835329712B632E7B7C792D2C8020,385D15;00046A7966444C7765,010C202F38520F70292E;70545282832E71291A7933192A5D5A5040,090C384F4520891D6B;0F11180006032A0D70332E011954828371152C202322,58477D63;0F111800037039450D2971332C632026,1F2E2B385282;003934357B7C88,0F20;00481F2023221E27262188,0F292C2E;18117900012C2E5B1F20,0F710D5229;000776776548,0F1118152E1F20;5254700001020612692D4E584647336375662E1F1E,71290D2620;006A583F232227261F20,0F29154703;00077088,522E1F8920;0F5C707971292C2E0E032A0D6A804B2D8B2B3348634C,521109154620;04795B3F651A5D,0F52010620;117154528283292C2E302D4E092A0D50407970443D,56804100;18115452830001712970802D2C2E302F2B2A0D78791F,0F20475861;0F1811000104037115454F7677657B7C392023222726210C,52092E1F;34350088,0F20;0F111800171A454F514E3A3871157765443D23221E262720,80612E1F;111800010206037939695482835D2D2E4E446375661F262120,0F52290D71;767779392623222788,152B1F1D20;000102060717706A33392D2E4E674447482322271E210C,71292B4F20;0F171511793F76584C,0347200C1D;000788,87;"
    };

    const vector<string> Taboo::HOUR_TABOO = {
        "0F520120,6D61;0F7082520115000255,80262F;707A000855,0102;0100380806,707A2E2C;0F8252150255,70717A7D01002C0306;0F707A0120002C0855,87;,87;0F71822952202C,7A7D0102;0F70825201150255,2E2C;0F295220380255,707A01000306;0F82295201200255,70717A7D2C;0F707A0120002C0855,80262F;707A000855,0102;0F708252150255,2E01002C0806;0F522055,707A01000306;0F700120002C380855,;0F825201150255,70717A7D802C262F;0F70718229527A202C55,0102;,87;0F702952200255,7A7D01000306;0F7082527A01150255,;0F70012000380255,80262F;000855,70717A7D012C02;0F7071527A2055,2E2C;0F522055,707A01000306;0F7001200255,;0F7071297A0115202C55,80262F;0F71822952002C380806,707A0102;0F70825201150255,7A2E2C;0F200255,707A01000306;,87;0F700120002C0855,7A7D80262F;0F7082527A1555,0102;0100380806,2E2C;0F20190255,70717A7D01002C0306;0F707A0120002C0855,87;0F520120,707A6D80262F61;0F7082521555,0102;0F7071297A0115200255,2E2C;0F2952200255,707A01000306;0F825201150255,70717A7D2C;0F7001200255,80262F;,87;,7A2E7D2C;0F712915202C55,707A01000306;0F7029018020002C38,;0F825201150255,70717A7D802C262F;0F70718229527A202C55,0102;0F71822952012002,2E6D2C61;0F7029527A200255,01000806;0F71290115202C,707A55;01002C380806,707A80262F;0F8252150055,70717A7D012C02;0F715220,707A2E2C55;,87;0F7001200255,7A7D;0F825201150255,707A80262F;0F7182295220002C380806,707A010255;0F718229522002,707A2E2C55;0F2952200255,707A01000306",
        "0F700120000255,;0F70290120000855,6D61;0F707129527A15802C381955,01000806;0F7101200019020655,707A2C;0F200855,707A2E01002C0306;0F7182520115802C0255,707A;0F700120000255,;,87;0F70297A01202C380955,;0F8252150255,707A01002C0306;0F0120000855,707A2E2C;0F7071297A01158020002C0855,;0F7082520115000255,;0F70717A01201955,6D61;0F7071295215802C3855,01000806;0F0120000206,707A2C;0F290120000855,707A2E2C;0F7082527A0115202C0255,;0100380806,2E2C;,87;0F707129527A011580202C380255,;0F7082520115000255,;0F71202C1955,707A;0F707129527A1556802C1955,;0F202C4B,707A01000306;0F71201955,707A6D61;0F70202C55,01000806;0F0120000206,707A2C;0F7101201955,707A2E2C;0F7129521556802C0255,707A01000306;01002C380806,;,87;0F7129527A0115802C380255,;0F82520115000255,707A2C;0F202C0855,707A01000306;0F7129521556802C1955,707A;0F700120002C0855,;202C,6D61;0F71295215802C3802,01000806;0F2002,707A01002C0306;0F29012002,707A2E2C;0F708229527A0115202C0255,;01002C380806,;,87;0F71295215802C380855,707A01000306;0F82520115000255,707A;0F0120000855,707A2E2C;0F707129527A1556802C1955,;0F7082520115000255,;,707A01000306;0F707129527A15802C3855,01000806;0F290120000855,707A2C;0F71201955,707A2E2C;0F7182520115802C02,707A55;0F202C0855,707A01000306;,87;0F0120002C086C,707A55;0F82520115000255,707A2C;0F2901202C3809,707A55;0F7129521556802C196C,707A0100030655",
        "0F70297A0120000855,80262F;0F822952202C,0102;0F71822952012002,7A2E7D2C;0F712915202C55,707A01000306;0F718229520120002C380802066C,707A;707A01000855,80262F;0F7082527A1555,0102;0F7001200255,2E2C;,87;0F7071297A15202C55,01000806;0F7082527A0115000255,80262F;0F82521555,70717A7D012C02;0F70718229527A200255,2E01002C0806;0F52,0120002C080306;0F7182295201202C02,7A7D;0F708252150255,01800026082F06;0F7071297A20002C38080655,0102;0F0120000855,707A2E2C;0F708252150255,0120002C080306;0F822952202C02,01000806;,87;0F7071297A15202C55,0102;0F708252011500380255,2E2C;0F202C4B,01000806;0F71822952202C0255,707A;0F7082527A01150255,80262F;0F71822952202C,7A7D016D02;0F712915202C,01800026082F06;0F71292055,707A01000306;,707A01000806;0F7082527A0115000255,80262F;0F71822952202C,0102;,87;0F712915202C,707A01000306;0F825201150255,707A;0F70825201150255,80262F;0F70717A201955,0102;0F7001200255,2E2C;,707A01000306;0F7071297A15202C55,01000806;0F70297A012000380855,80262F;0F2920,70717A7D016D2C02;0F708252150255,2E01002C0806;,707A01000306;,87;0F7082527A01150255,2E2C;0F8252150038,707A010255;0F82520115000255,70717A7D2C;,707A01000306;0F5220,01000806;0F70297A0120000855,80262F;0F712915202C,016D000806;0F70718229527A01200038080206,2E2C;0F822952202C02,707A01000306;0F825201150255,707A;0F7082527A01150255,80262F;,87;0F7071297A011520002C55,;0F708252150255,01000806;0F8252150255,70717A7D01002C0806",
        "0F71822952202C02,707A;0F7029527A0120000255,;0F7071527A202C55,87;0F71295215802C3802,707A01000306;55,707A01000806;0F292055,707A2E2C;0F708229527A202C0255,01000806;0F708252150255,01000806;0F202C0855,707A01000306;,87;0F8229520115200255,707A2C;0F7082527A01150255,;0F7129521556802C1955,707A;2C3808,707A01000306;70297A0120002C0855,87;29202C,6D61;0F708229527A202C0255,01000806;0F71522055,707A2E01002C0806;0F7129521556802C0855,707A01000306;0F7082520115000255,;0F71290120002C080206,87;,87;0F0120004B,707A2C;0F8252150255,707A2E01002C0306;71295201155680002C0855,707A;0F70290120002C06,;0F822952202C0255,01000806;71295215802C38,01000806;0F7071822952202C0255,01000806;0F70718229520120002C080255,;0F70718229527A202C0255,;0F7082527A01150255,;0F70717A01200008190655,87;,87;0F8229520115200255,707A2C;0F708229527A0115202C0255,;0F707129527A011556802C026C,;2C38,01000806;0F202C0855,707A01000306;0F70712952011580202C380255,;0F718229522002,707A01002C0806;0F295201200255,707A2E2C;0F707129527A011556802C026C,;0F8252150255,707A01000306;0F70297A01202C0255,87;,87;0F8252150255,707A01002C0806;0F8252150255,707A2E01002C0806;0F712952155680202C0255,707A01000306;0F7029012000080255,;0F70718229527A202C0255,87;0F707129527A0180202C0255,;,707A01000806;0F822952202C02,707A01000306;707129527A01155680002C0855,;0F70822952011520002C0255,;0F822952202C02,01000806;,87;0F8252150255,707A01002C0306;0F82520115000255,707A2E2C",
        "0F82521555,70717A7D016D2C02;,2E2C;0F5220,707A01000306;0F0120000206,707A;0F825201150255,707A80262F;0F70717A202C381955,01000806;0F0120000206,70717A7D2C;0F7129202C1955,707A01000306;0F7082527A0115000255,;0F7082520115000255,80262F;,87;0F70717A0120003808190655,2E2C;0F8252150255,707A01000306;,2E2C;0F7082527A01150255,80262F;0F70200006,7A7D016D02;0F7071297A01152055,2E6D2C41;2C38,01000806;0F290120000855,70717A7D2C;0F70825201150255,80262F;0F702920002C,0102;0F0120004B,2E2C;,87;0F70717A0120002C3808190655,;0F7082520115000255,80262F;,0102;0F7071527A01200655,2E2C;0F702002,7A7D01000306;0F7129011520002C,6D41;0F70825215380255,01800026082F06;0F200006,70717A7D016D2C02;,2E2C;0F82521502,707A0100030655;0F7082520115000255,7A7D;,87;0F70717A20002C3808190655,016D02;0F825201150255,70717A7D2C;0F7129202C1955,707A01000306;0F520120006C,707A55;0F7082520115000255,7A7D80262F;0F712915202C,707A016D02;0F70717A20381955,2E01002C0806;0F29202C02,707A01000306;0F82295201202C02,707A;0F70825201150255,80262F;0F7082521555,7A7D016D02;,87;0F71202C38081955,707A01000306;0F82520115000255,70717A7D2C;0F70825201150255,80262F;0F70527A2055,016D02;0F0120000206,707A2E2C;0F712915202C,707A016D00034106;2C38,707A01000806;0F82520115000255,70717A7D802C262F;0F822952202C,0102;0F7082527A01150255,2E2C;0F708252150255,7A7D01000306;,87;01002C380806,80262F",
        "0F8229520102,707A202C03;0F7182520115802C0255,707A;0F29522055,707A01000306;0F70718229527A01202C0255,87;0F707129527A15803855,202C03;0F71822952200255,707A2C03;0F71822952012002,707A2E2C;0F7129521556802C0855,707A01000306;0F700120000255,;0F70010255,202C03;0F707129520115800038,202C03;,87;0F29522055,707A2E01002C0306;0F707182527A0115802C0255,;0F70297A55,202C03;0F7071297A0255,202C03;0F718229520120002C080206,;0F29200855,707A01000306;55,707A2E01002C0806;0F7071295201155680000855,202C03;0F01004B,202C03;0F825201150255,707A;0F7129521580202C3855,707A01000306;,87;0F70297A55,202C03;0F707182520115800255,202C03;0F7182295201002C38080206,;0F29200855,707A01000306;0F70297A202C02,01000806;0F70297A01000855,202C03;0F712920,707A2E01002C080306;0F7129521556802C55,707A;0F8252150255,707A01000306;0F8252011502,707A55;0F29526C,707A202C0355;,87;0F718229522002,707A2E2C;0F71825215802C0255,707A01000306;0F70202C55,01000806;0F707A01000855,202C03;0F707129520115800038,202C03;0F290180202C380955,707A6D61;0F7029527A2055,01000806;0F707129527A1556802C0255,;0F71520106,707A202C03;0F70825201150255,202C03;0F718229520120002C080206,;,87;0F200255,707A2E01002C0806;0F70718252011580000255,202C03;0F715201066C,707A202C03;0F290180202C380955,707A;0F71295215802C3855,707A01000306;0F70717A20190255,016D00086106;0F712952,0120002C080306;0F70718229527A0255,202C03;0F7182295201002C38080206,707A;0F8252150255,707A01000306;0F70202C55,01000806;,87",
        ",87;0F822952200255,70717A7D01002C0306;0F70825201150255,87;0F7082527A01150255,2E2C;0F70202C55,7A7D0102;0F7029527A012055,2E2C;0F2920380855,707A01000306;0F82520115000255,70717A7D2C;0F7082527A01150255,80262F;0F52202C,016D02;0F70825201150255,7A2E7D2C;0F71822952202C0255,707A01000306;,87;0F825201150255,70717A7D802C262F;0F712952202C,0102;0F70527A012055,2E2C;700855,7A7D01000306;0F7071297A011520002C55,;0F8252011500380255,707A80262F;0F7082521555,010002;0F71290120,707A2E2C;0F71822952202C02,707A0100030655;0F7082527A01150255,;0F7001200255,2E2C;,87;0F707A01200255,2E2C;0F82521502,707A0100030655;0F0120002C086C,707A55;0F7082527A01150255,80262F;0F712915202C,016D02;0F2901203809,707A2E2C;0F708252150255,01000806;0F7182295201202C02,87;0F7082527A01150255,80262F;0F7082527A1555,0102;0F7071297A01152055,2E2C;,87;0F707A01200255,;0F70825201150255,80262F;0F52202C,016D02;0F71522055,707A2E2C;0F712915202C02,707A01000306;0F718229520120002C3802,6D41;0F7082527A150255,01800026082F06;0F71822952202C,0102;0F70527A012055,2E2C;0855,707A01000306;707A01000855,;,87;0F522055,70717A7D012C02;0F825201150255,707A2E2C;0F71822952202C02,707A01000306;0F707182295201202C0255,7A7D;0F7082527A01150255,80262F;0F71822952002C380806,016D0241;0F70200255,2E01002C0806;0855,707A01000306;0F700120002C0855,;0F70825201150255,7A7D80262F;0F712915202C,016D02",
        "0F290120002C0855,707A;,87;0F7071295201155680002C0206,;0F70822952011520002C0255,;0F70297A01202C0255,87;0F70712901158020002C380855,;0F8252150255,707A01000306;0F71201955,707A2E6D2C61;0F70297A0120002C0206,;0F710120002C3808190655,707A;0F290120002C0855,707A;0F71825215802C380255,707A01000306;,707A2E2C;,87;0F7071295201155680002C0855,;0F7082520115000255,;0F7120081955,707A01000306;0F7071295201158020002C380206,;0F0120004B,707A2C;0F71201955,707A;0F707129521556802C55,01000806;2C3808,707A0100030655;0F71201955,707A;0F70822952011520002C0255,;0F01200255,707A2C;,87;0F7129521556802C0855,707A01000306;0F70290120002C,;0F2901202C,87;0F71295201158020002C380206,707A;0F01200255,707A2C;0F71201955,707A2E016D002C036106;0F7129521556802C02,01000806;0F8229520120002C02,;0F70297A01202C0255,87;0F7082520115000255,;0F202C0855,707A01000306;,87;0F7071297A0120002C19020655,;0F82520115000255,707A;0F70297A01202C0255,87;0F71295215802C3802,707A01000306;0F825201150255,707A2C;0F71201955,707A2E2C;0F70712952155680202C0255,01000806;0F70290120000855,;0F202C0855,707A01000306;0F70718252011580002C380255,;0F7129202C1955,707A;,87;0F707129520115568020002C0255,;0F8252150255,707A01000306;0F70717A201955,2E2C;0F70290120002C0206,;0F01200255,707A2C;0F290120000855,707A2E6D2C61;0F707129521556802C55,01000806;01002C380806,;0F71201955,707A;0F718252011580002C380255,707A",
        "0F708252150255,01000806;0F707A010255,202C03;,87;2920002C0806,0102;0F7082527A01150255,2E2C;0F200255,707A01000306;0F7029527A0155,202C03;0F7082527A011500380255,80262F;0F82295220,70717A7D016D2C02;0F70717A201955,2E01002C0806;0F70527A2055,01000806;707A55,0120002C080306;0F70825201150255,80262F;002C380806,707A010255;,87;0F2002,707A0100030655;0F7029527A010255,202C03;0F0120002C4B02,80262F;0F7071297A15202C55,0102;0F70717A0120003808190655,2E2C;0F8229522002,70717A7D01002C0806;707A55,0120002C080306;0F7082527A01150255,80262F;29202C,7A7D016D02;0F7082520115000255,2E2C;0F71202C38081955,707A01000306;,87;0F70825201150255,80262F;0F7082521555,0102;0F7082527A01150255,2E2C;0F7071297A15202C55,01000806;0F700255,0120002C080306;0F825201150255,70717A7D6D802C262F61;,016D0002;0F70297A0120000855,2E2C;0F70200255,7A7D01000306;0F708229527A01150255,202C03;0F7082527A011500380255,80262F;,87;0F70717A201955,2E2C;0F708252150255,01000806;0F708252150255,0120002C080306;0F7082527A01150255,80262F;0F70717A2000381955,0102;0F290120000855,70717A7D2C;0F70202C55,01000806;0F70527A0155,202C03;0F70825201150255,7A7D80262F;0F7082521555,0102;0F70717A0120003808190655,2E2C;,87;0F7055,0120002C080306;0F70825201150255,80262F;0F7082521555,7A7D016D02;0F70297A0120000855,2E2C;0F71202C38081955,707A01000306;0F29522055,70717A7D2C03;0F708252150255,01800026082F06;0F522055,707A0102;0F71201955,707A2E2C",
        "71295215802C380855,707A01000306;0F825201150255,707A2C;0F822952011520000255,707A2E2C;,87;0F708252150255,01000806;0F8252150255,707A01000306;0F70718229527A202C0255,;0F292055,707A2C;0F82295201200255,707A2E2C;0F707129527A011556802C55,6D41;707A55,01000806;,707A55;0F70297A0120002C0855,;0F7082520115000255,;,707A2E01002C0806;,87;0F718229520120002C080206,;0F825201150255,707A;0F82295201202C02,707A55;,707A55;0855,707A2E01002C0306;0F707129527A1556802C55,6D41;0F7029202C55,01000806;0F700120002C0855,87;0F707129527A15802C3855,01000806;0F708252150255,01000806;0F71822952012002,707A2E2C;,87;0F7082520115000255,;0F825201150255,707A;71295215802C380855,707A01000306;,707A2C;0F0120000855,707A2E2C;0F7029012000080255,6D41;0F707152202C,01000806;0F71822952202C02,01000806;0F70718229527A01202C0255,;0F708229527A0115202C0255,;0F8229520120000255,707A2E2C;,87;0F8252150255,707A01000306;0F70825201150255,87;0F707A0120002C0855,;0F700120002C080255,;,707A2E01002C0806;0F71822952202C02,01000806;0F718229522C3802,01000806;0F2952202C55,707A;0F707129527A0115802C380255,;0F2901802000,707A2C;0855,707A2E01002C0306;,87;0F70822952011520002C0255,;0F7082520115000255,;0F707129527A15802C380255,01000806;0F718229522002,707A01002C0806;0F7182295201200002,707A2E2C;0F702901202C0255,6D41;2C38,01000806;0F7071297A2055,87",
        "0F7071201955,7A7D;0F71290115202C,707A80262F;0F70825215003855,016D02;0F825201150255,70717A7D2C;,87;0F520120,707A55;0F825201150255,707A80262F;0F71291520002C,016D02;0100380806,707A2E2C55;0F71201955,707A01000306;0F71295201202C,6D61;0F5220,01800026082F06;0F7071201955,7A7D016D02;0F7129011520,707A2E2C;2C3808,707A01000306;0F0120004B,70717A7D2C;,87;0F5220,016D02;0F70825201150255,7A2E7D2C;0F712915202C,707A01000306;0F70717A0120002C3808190655,;0F7082527A01150255,80262F;0F70200006,016D0261;0F5220,2E01002C0806;0F71201955,707A01000306;0F7129011520002C,707A;0F708252011500380255,80262F;0F82521555,70717A7D016D2C02;,87;0F5220,707A01000306;0F70825201150255,7A7D;0F825201150255,707A80262F;002C380806,016D02;0F201955,70717A7D2C;0F2002,707A016D00036106;0F5220,01000806;0F825201150255,707A80262F;0F71291520002C,016D02;0F708252011500380255,2E2C;0F8252150255,70717A7D01002C0306;,87;0F7082527A01150255,80262F;0F7082527A1555,016D02;0F7129011520,707A2E2C;2C3808,707A01000306;0F71201955,707A;0F7082520115000255,80262F;0F5220,016D0002;0F7071201955,7A2E7D2C;0F712915202C,707A01000306;0F7082527A011500380255,;0F825201150255,70717A7D802C262F;,87;0F7071520120002C06,;0F29202C55,707A01000306;0F7129011520002C,707A;0F8252011500380255,707A80262F;0F71201955,707A0102;0F700120000206,6D61;0F5220,01000806",
        "0F7182295201202C02,707A55;0F7029527A0120002C0255,;0F82520115000255,707A2C03;0F82520115000255,707A2E2C;0F7129521556802C1955,707A01000306;,87;0F7129202C196C,707A55;0F7182520115803802,707A202C0355;0F29522055,707A2C;0F71822952200255,707A2E01002C0306;0F718229520120002C080206,;0F290120000855,707A;0F700255,0120002C080306;707129527A011580002C380855,;0F204B,707A01002C0306;0F825201150255,707A;0F7071297A202C1955,;,87;0F707A01200255,87;0F71825215802C380255,707A01000306;0F7182295201202C02,;0F290120000855,707A2E2C;0F7071295201155680000855,202C03;01000855,707A;0F70717A201955,01000806;0F7129520115802C3855,707A;0F8229520115200255,707A2C;0F825201150255,707A2E2C03;0F707129527A1580202C55,;,87;0F70718229527A202C0255,87;0F70718252011580002C380255,;0F01200255,707A2C03;01000855,707A2E2C;0F7129521556802C1955,707A01000306;01002C380806,707A6D41;0F7071297A202C1955,01000806;0F707129527A15803855,202C03;0F01200255,707A2C;0F8252150255,707A01000306;0F71822952202C02,;,87;0F707A010255,202C03;707129527A011580002C380855,;0F29202C3809,707A01000306;0F71201955,707A;0F7071297A202C1955,;0100380806,6D202C0341;0F7029527A2055,01000806;0F71295215802C3855,707A01000306;0F825201150255,707A2C;0F822952011520000255,707A2E2C;0F7071295201155680000855,202C03;,87;0F29202C380955,707A01000306;0F7182520115802C380255,707A;29202C,707A;0F71292055,707A2E2C03;0F707129527A1556802C55,;0F71822952202C02,707A016D00034106"
    };

    Taboo Taboo::from_index(const int index) {
        return Taboo(index);
    }

    Taboo Taboo::from_name(const string &name) {
        return Taboo(name);
    }

    Taboo Taboo::next(const int n) const {
        return from_index(next_index(n));
    }

    vector<Taboo> Taboo::get_taboos(vector<string> data, int sup_index, int sub_index, int index) {
        auto l = vector<Taboo>();
        istringstream sp(data[sup_index]);
        string sub;
        int x = 0;
        while (getline(sp, sub, ';')) {
            if (sub_index == x) {
                istringstream sb(sub);
                string d;
                int y = 0;
                while (getline(sb, d, ',')) {
                    if (index == y) {
                        for (unsigned long i = 0, j = d.size(); i < j; i += 2) {
                            l.push_back(from_index(stoi(d.substr(i, 2), nullptr, 16)));
                        }
                        break;
                    }
                    y++;
                }
                break;
            }
            x++;
        }
        return l;
    }

    vector<Taboo> Taboo::get_day_recommends(const SixtyCycle &month, const SixtyCycle &day) {
        return get_taboos(DAY_TABOO, month.get_earth_branch().get_index(), day.get_index(), 0);
    }

    vector<Taboo> Taboo::get_day_avoids(const SixtyCycle &month, const SixtyCycle &day) {
        return get_taboos(DAY_TABOO, month.get_earth_branch().get_index(), day.get_index(), 1);
    }

    vector<Taboo> Taboo::get_hour_recommends(const SixtyCycle &day, const SixtyCycle &hour) {
        return get_taboos(HOUR_TABOO, hour.get_earth_branch().get_index(), day.get_index(), 0);
    }

    vector<Taboo> Taboo::get_hour_avoids(const SixtyCycle &day, const SixtyCycle &hour) {
        return get_taboos(HOUR_TABOO, hour.get_earth_branch().get_index(), day.get_index(), 1);
    }

    const vector<string> Ten::NAMES = {
        "甲子", "甲戌", "甲申", "甲午", "甲辰", "甲寅"
    };

    Ten Ten::from_index(const int index) {
        return Ten(index);
    }

    Ten Ten::from_name(const string &name) {
        return Ten(name);
    }

    Ten Ten::next(const int n) const {
        return from_index(next_index(n));
    }

    const vector<string> Terrain::NAMES = {
        "长生", "沐浴", "冠带", "临官", "帝旺", "衰", "病", "死", "墓", "绝", "胎", "养"
    };

    Terrain Terrain::from_index(const int index) {
        return Terrain(index);
    }

    Terrain Terrain::from_name(const string &name) {
        return Terrain(name);
    }

    Terrain Terrain::next(const int n) const {
        return from_index(next_index(n));
    }

    const vector<string> Twenty::NAMES = {
        "一运", "二运", "三运", "四运", "五运", "六运", "七运", "八运", "九运"
    };

    Twenty Twenty::from_index(const int index) {
        return Twenty(index);
    }

    Twenty Twenty::from_name(const string &name) {
        return Twenty(name);
    }

    Twenty Twenty::next(const int n) const {
        return from_index(next_index(n));
    }

    Sixty Twenty::get_sixty() const {
        return Sixty::from_index(index / 3);
    }

    const vector<string> Week::NAMES = {
        "日", "一", "二", "三", "四", "五", "六"
    };

    Week Week::from_index(const int index) {
        return Week(index);
    }

    Week Week::from_name(const string &name) {
        return Week(name);
    }

    Week Week::next(const int n) const {
        return from_index(next_index(n));
    }

    SevenStar Week::get_seven_star() const {
        return SevenStar::from_index(index);
    }

    const vector<string> Zodiac::NAMES = {
        "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"
    };

    Zodiac Zodiac::from_index(const int index) {
        return Zodiac(index);
    }

    Zodiac Zodiac::from_name(const string &name) {
        return Zodiac(name);
    }

    Zodiac Zodiac::next(const int n) const {
        return from_index(next_index(n));
    }

    bool Zodiac::equals(const Zodiac &other) const {
        return to_string() == other.to_string();
    }

    EarthBranch Zodiac::get_earth_branch() const {
        return EarthBranch::from_index(index);
    }

    const vector<string> Zone::NAMES = {
        "东", "北", "西", "南"
    };

    Zone Zone::from_index(const int index) {
        return Zone(index);
    }

    Zone Zone::from_name(const string &name) {
        return Zone(name);
    }

    Zone Zone::next(const int n) const {
        return from_index(next_index(n));
    }

    Direction Zone::get_direction() const {
        return Direction::from_name(get_name());
    }

    Beast Zone::get_best() const {
        return Beast::from_index(index);
    }

    const vector<string> KitchenGodSteed::NUMBERS = {
        "一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二"
    };

    KitchenGodSteed KitchenGodSteed::from_lunar_year(const int lunar_year) {
        return KitchenGodSteed(lunar_year);
    }

    string KitchenGodSteed::by_heaven_stem(const int n) const {
        return NUMBERS[first_day_sixty_cycle.get_heaven_stem().steps_to(n)];
    }

    string KitchenGodSteed::by_earth_branch(int n) const {
        return NUMBERS[first_day_sixty_cycle.get_earth_branch().steps_to(n)];
    }

    string KitchenGodSteed::get_mouse() const {
        return by_earth_branch(0) + "鼠偷粮";
    }

    string KitchenGodSteed::get_grass() const {
        return "草子" + by_earth_branch(0) + "分";
    }

    string KitchenGodSteed::get_cattle() const {
        return by_earth_branch(1) + "牛耕田";
    }

    string KitchenGodSteed::get_flower() const {
        return "花收" + by_earth_branch(3) + "分";
    }

    string KitchenGodSteed::get_dragon() const {
        return by_earth_branch(4) + "龙治水";
    }

    string KitchenGodSteed::get_horse() const {
        return by_earth_branch(6) + "马驮谷";
    }

    string KitchenGodSteed::get_chicken() const {
        return by_earth_branch(9) + "鸡抢米";
    }

    string KitchenGodSteed::get_silkworm() const {
        return by_earth_branch(9) + "姑看蚕";
    }

    string KitchenGodSteed::get_pig() const {
        return by_earth_branch(11) + "屠共猪";
    }

    string KitchenGodSteed::get_field() const {
        return "甲田" + by_heaven_stem(0) + "分";
    }

    string KitchenGodSteed::get_cake() const {
        return by_heaven_stem(2) + "人分饼";
    }

    string KitchenGodSteed::get_gold() const {
        return by_heaven_stem(7) + "日得金";
    }

    string KitchenGodSteed::get_people_cakes() const {
        return by_earth_branch(2) + "人" + by_heaven_stem(2) + "丙";
    }

    string KitchenGodSteed::get_people_hoes() const {
        return by_earth_branch(2) + "人" + by_heaven_stem(3) + "锄";
    }

    string KitchenGodSteed::get_name() const {
        return "灶马头";
    }

    const vector<string> Dog::NAMES = {
        "初伏", "中伏", "末伏"
    };

    Dog Dog::from_index(const int index) {
        return Dog(index);
    }

    Dog Dog::from_name(const string &name) {
        return Dog(name);
    }

    Dog Dog::next(const int n) const {
        return from_index(next_index(n));
    }

    Dog DogDay::get_dog() const {
        return dog;
    }

    int DogDay::get_day_index() const {
        return day_index;
    }

    string DogDay::get_name() const {
        return dog.get_name();
    }

    string DogDay::to_string() const {
        return dog.to_string() + "第" + std::to_string(day_index + 1) + "天";
    }

    const vector<string> FetusHeavenStem::NAMES = {
        "门", "碓磨", "厨灶", "仓库", "房床"
    };

    FetusHeavenStem FetusHeavenStem::from_index(const int index) {
        return FetusHeavenStem(index);
    }

    FetusHeavenStem FetusHeavenStem::next(const int n) const {
        return from_index(next_index(n));
    }

    const vector<string> FetusEarthBranch::NAMES = {
        "碓", "厕", "炉", "门", "栖", "床"
    };

    FetusEarthBranch FetusEarthBranch::from_index(const int index) {
        return FetusEarthBranch(index);
    }

    FetusEarthBranch FetusEarthBranch::next(const int n) const {
        return from_index(next_index(n));
    }

    const vector<string> FetusMonth::NAMES = {
        "占房床", "占户窗", "占门堂", "占厨灶", "占房床", "占床仓", "占碓磨", "占厕户", "占门房", "占房床", "占灶炉", "占房床"
    };

    optional<FetusMonth> FetusMonth::from_lunar_month(const LunarMonth& lunar_month) {
        if (!lunar_month.is_leap()) {
            return FetusMonth(lunar_month.get_month() - 1);
        }
        return nullopt;
    }

    FetusMonth FetusMonth::next(const int n) const {
        return FetusMonth(next_index(n));
    }

    FetusDay FetusDay::from_sixty_cycle_day(const SixtyCycleDay& sixty_cycle_day) {
        return FetusDay(sixty_cycle_day);
    }

    FetusDay FetusDay::from_lunar_day(const LunarDay &lunar_day) {
        return FetusDay(lunar_day);
    }

    string FetusDay::get_name() const {
        string s = fetus_heaven_stem.get_name() + fetus_earth_branch.get_name();
        string door = "门";
        if ("门门" == s) {
            s = "占大门";
        } else if ("碓磨碓" == s) {
            s = "占碓磨";
        } else if ("房床床" == s) {
            s = "占房床";
        } else if (s.compare(0, door.size(), door) == 0) {
            s = "占" + s;
        }

        s += " ";

        s += Side::IN == side ? "房内" : "外";

        string ds = "北南西东";
        string direction_name = direction.get_name();
        if (Side::OUT == side && ds.find(direction_name) != string::npos) {
            s += "正";
        }
        s += direction_name;
        return s;
    }

    Side FetusDay::get_side() const {
        return side;
    }

    Direction FetusDay::get_direction() const {
        return direction;
    }

    FetusHeavenStem FetusDay::get_fetus_heaven_stem() const {
        return fetus_heaven_stem;
    }

    FetusEarthBranch FetusDay::get_fetus_earth_branch() const {
        return fetus_earth_branch;
    }

    const vector<string> Nine::NAMES = {
        "一九", "二九", "三九", "四九", "五九", "六九", "七九", "八九", "九九"
    };

    Nine Nine::from_index(const int index) {
        return Nine(index);
    }

    Nine Nine::from_name(const string &name) {
        return Nine(name);
    }

    Nine Nine::next(const int n) const {
        return from_index(next_index(n));
    }

    Nine NineDay::get_nine() const {
        return nine;
    }

    int NineDay::get_day_index() const {
        return day_index;
    }

    string NineDay::get_name() const {
        return nine.get_name();
    }

    string NineDay::to_string() const {
        return nine.to_string() + "第" + std::to_string(day_index + 1) + "天";
    }

    const vector<string> PengZuHeavenStem::NAMES = {
        "甲不开仓财物耗散", "乙不栽植千株不长", "丙不修灶必见灾殃", "丁不剃头头必生疮", "戊不受田田主不祥", "己不破券二比并亡", "庚不经络织机虚张", "辛不合酱主人不尝", "壬不泱水更难提防", "癸不词讼理弱敌强"
    };

    PengZuHeavenStem PengZuHeavenStem::from_index(const int index) {
        return PengZuHeavenStem(index);
    }

    PengZuHeavenStem PengZuHeavenStem::from_name(const string &name) {
        return PengZuHeavenStem(name);
    }

    PengZuHeavenStem PengZuHeavenStem::next(const int n) const {
        return from_index(next_index(n));
    }

    const vector<string> PengZuEarthBranch::NAMES = {
        "子不问卜自惹祸殃", "丑不冠带主不还乡", "寅不祭祀神鬼不尝", "卯不穿井水泉不香", "辰不哭泣必主重丧", "巳不远行财物伏藏", "午不苫盖屋主更张", "未不服药毒气入肠", "申不安床鬼祟入房", "酉不会客醉坐颠狂", "戌不吃犬作怪上床", "亥不嫁娶不利新郎"
    };

    PengZuEarthBranch PengZuEarthBranch::from_index(const int index) {
        return PengZuEarthBranch(index);
    }

    PengZuEarthBranch PengZuEarthBranch::from_name(const string &name) {
        return PengZuEarthBranch(name);
    }

    PengZuEarthBranch PengZuEarthBranch::next(const int n) const {
        return from_index(next_index(n));
    }

    PengZu PengZu::from_sixty_cycle(const SixtyCycle &sixty_cycle) {
        return PengZu(sixty_cycle);
    }

    string PengZu::get_name() const {
        return peng_zu_heaven_stem.to_string() + " " + peng_zu_earth_branch.to_string();
    }

    PengZuHeavenStem PengZu::get_peng_zu_heaven_stem() const {
        return peng_zu_heaven_stem;
    }

    PengZuEarthBranch PengZu::get_peng_zu_earth_branch() const {
        return peng_zu_earth_branch;
    }

    const vector<string> Phenology::NAMES = {
        "蚯蚓结", "麋角解", "水泉动", "雁北乡", "鹊始巢", "雉始雊", "鸡始乳", "征鸟厉疾", "水泽腹坚", "东风解冻", "蛰虫始振", "鱼陟负冰", "獭祭鱼", "候雁北", "草木萌动", "桃始华", "仓庚鸣", "鹰化为鸠", "玄鸟至", "雷乃发声", "始电", "桐始华", "田鼠化为鴽", "虹始见", "萍始生", "鸣鸠拂其羽", "戴胜降于桑", "蝼蝈鸣", "蚯蚓出", "王瓜生", "苦菜秀", "靡草死", "麦秋至", "螳螂生", "鵙始鸣", "反舌无声", "鹿角解", "蜩始鸣", "半夏生", "温风至", "蟋蟀居壁", "鹰始挚", "腐草为萤", "土润溽暑", "大雨行时", "凉风至", "白露降", "寒蝉鸣", "鹰乃祭鸟", "天地始肃", "禾乃登", "鸿雁来", "玄鸟归", "群鸟养羞", "雷始收声", "蛰虫坯户", "水始涸", "鸿雁来宾", "雀入大水为蛤", "菊有黄花", "豺乃祭兽", "草木黄落", "蛰虫咸俯", "水始冰", "地始冻", "雉入大水为蜃", "虹藏不见", "天气上升地气下降", "闭塞而成冬", "鹖鴠不鸣", "虎始交", "荔挺出"
    };

    Phenology Phenology::from_index(const int year, const int index) {
        return Phenology(year, index);
    }

    Phenology Phenology::from_name(const int year, const string &name) {
        return Phenology(year, name);
    }

    Phenology Phenology::next(const int n) const {
        const int size = get_size();
        const int i = index + n;
        return from_index((year * size + i) / size, index_of(i));
    }

    int Phenology::get_year() const {
        return year;
    }

    JulianDay Phenology::get_julian_day() const {
        const double t = ShouXingUtil::sa_lon_t((year - 2000 + (index - 18) * 5.0 / 360 + 1) * 2 * M_PI);
        return JulianDay::from_julian_day(t * 36525 + JulianDay::J2000 + 8.0 / 24 - ShouXingUtil::dt_t(t * 36525));
    }

    ThreePhenology Phenology::get_three_phenology() const {
        return ThreePhenology::from_index(index % 3);
    }

    Phenology PhenologyDay::get_phenology() const {
        return phenology;
    }

    int PhenologyDay::get_day_index() const {
        return day_index;
    }

    string PhenologyDay::get_name() const {
        return phenology.get_name();
    }

    string PhenologyDay::to_string() const {
        return phenology.to_string() + "第" + std::to_string(day_index + 1) + "天";
    }

    const vector<string> ThreePhenology::NAMES = {
        "初候", "二候", "三候"
    };

    ThreePhenology ThreePhenology::from_index(const int index) {
        return ThreePhenology(index);
    }

    ThreePhenology ThreePhenology::from_name(const string &name) {
        return ThreePhenology(name);
    }

    ThreePhenology ThreePhenology::next(const int n) const {
        return from_index(next_index(n));
    }

    const vector<string> PlumRain::NAMES = {
        "入梅", "出梅"
    };

    PlumRain PlumRain::from_index(const int index) {
        return PlumRain(index);
    }

    PlumRain PlumRain::from_name(const string &name) {
        return PlumRain(name);
    }

    PlumRain PlumRain::next(const int n) const {
        return from_index(next_index(n));
    }

    PlumRain PlumRainDay::get_plum_rain() const {
        return plum_rain;
    }

    int PlumRainDay::get_day_index() const {
        return day_index;
    }

    string PlumRainDay::get_name() const {
        return plum_rain.get_name();
    }

    string PlumRainDay::to_string() const {
        return get_plum_rain().get_index() == 0 ? plum_rain.to_string() + "第" + std::to_string(day_index + 1) + "天" : get_name();
    }

    const vector<string> MinorRen::NAMES = {
        "大安", "留连", "速喜", "赤口", "小吉", "空亡"
    };

    MinorRen MinorRen::from_index(const int index) {
        return MinorRen(index);
    }

    MinorRen MinorRen::from_name(const string &name) {
        return MinorRen(name);
    }

    MinorRen MinorRen::next(const int n) const {
        return from_index(next_index(n));
    }

    Luck MinorRen::get_luck() const {
        return Luck::from_index(index % 2);
    }

    Element MinorRen::get_element() const {
        const int indices[6] = {0, 4, 1, 3, 0, 2};
        return Element::from_index(indices[index]);
    }

    const vector<string> Dipper::NAMES = {
        "天枢", "天璇", "天玑", "天权", "玉衡", "开阳", "摇光", "洞明", "隐元"
    };

    Dipper Dipper::from_index(const int index) {
        return Dipper(index);
    }

    Dipper Dipper::from_name(const string &name) {
        return Dipper(name);
    }

    Dipper Dipper::next(const int n) const {
        return from_index(next_index(n));
    }

    const vector<string> NineStar::NAMES = {
        "一", "二", "三", "四", "五", "六", "七", "八", "九"
    };

    NineStar NineStar::from_index(const int index) {
        return NineStar(index);
    }

    NineStar NineStar::from_name(const string &name) {
        return NineStar(name);
    }

    NineStar NineStar::next(const int n) const {
        return from_index(next_index(n));
    }

    string NineStar::get_color() const {
        string colors[9] = {"白", "黑", "碧", "绿", "黄", "白", "赤", "白", "紫"};
        return colors[index];
    }

    Element NineStar::get_element() const {
        const int indices[9] = {4, 2, 0, 0, 2, 3, 3, 2, 1};
        return Element::from_index(indices[index]);
    }

    Dipper NineStar::get_dipper() const {
        return Dipper::from_index(index);
    }

    Direction NineStar::get_direction() const {
        return Direction::from_index(index);
    }

    string NineStar::to_string() const {
        return get_name() + get_color() + get_element().to_string();
    }

    const vector<string> SevenStar::NAMES = {
        "日", "月", "火", "水", "木", "金", "土"
    };

    SevenStar SevenStar::from_index(const int index) {
        return SevenStar(index);
    }

    SevenStar SevenStar::from_name(const string &name) {
        return SevenStar(name);
    }

    SevenStar SevenStar::next(const int n) const {
        return from_index(next_index(n));
    }

    Week SevenStar::get_week() const {
        return Week::from_index(index);
    }

    const vector<string> SixStar::NAMES = {
        "先胜", "友引", "先负", "佛灭", "大安", "赤口"
    };

    SixStar SixStar::from_index(const int index) {
        return SixStar(index);
    }

    SixStar SixStar::from_name(const string &name) {
        return SixStar(name);
    }

    SixStar SixStar::next(const int n) const {
        return from_index(next_index(n));
    }

    const vector<string> TenStar::NAMES = {
        "比肩", "劫财", "食神", "伤官", "偏财", "正财", "七杀", "正官", "偏印", "正印"
    };

    TenStar TenStar::from_index(const int index) {
        return TenStar(index);
    }

    TenStar TenStar::from_name(const string &name) {
        return TenStar(name);
    }

    TenStar TenStar::next(const int n) const {
        return from_index(next_index(n));
    }

    const vector<string> Ecliptic::NAMES = {
        "黄道", "黑道"
    };

    Ecliptic Ecliptic::from_index(const int index) {
        return Ecliptic(index);
    }

    Ecliptic Ecliptic::from_name(const string &name) {
        return Ecliptic(name);
    }

    Ecliptic Ecliptic::next(const int n) const {
        return from_index(next_index(n));
    }

    Luck Ecliptic::get_luck() const {
        return Luck::from_index(index);
    }

    const vector<string> TwelveStar::NAMES = {
        "青龙", "明堂", "天刑", "朱雀", "金匮", "天德", "白虎", "玉堂", "天牢", "玄武", "司命", "勾陈"
    };

    TwelveStar TwelveStar::from_index(const int index) {
        return TwelveStar(index);
    }

    TwelveStar TwelveStar::from_name(const string &name) {
        return TwelveStar(name);
    }

    TwelveStar TwelveStar::next(const int n) const {
        return from_index(next_index(n));
    }

    Ecliptic TwelveStar::get_ecliptic() const {
        const int indices[12] = {0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1};
        return Ecliptic::from_index(indices[index]);
    }

    const vector<string> TwentyEightStar::NAMES = {
        "角", "亢", "氐", "房", "心", "尾", "箕", "斗", "牛", "女", "虚", "危", "室", "壁", "奎", "娄", "胃", "昴", "毕", "觜", "参", "井", "鬼", "柳", "星", "张", "翼", "轸"
    };

    TwentyEightStar TwentyEightStar::from_index(const int index) {
        return TwentyEightStar(index);
    }

    TwentyEightStar TwentyEightStar::from_name(const string &name) {
        return TwentyEightStar(name);
    }

    TwentyEightStar TwentyEightStar::next(const int n) const {
        return from_index(next_index(n));
    }

    SevenStar TwentyEightStar::get_seven_star() const {
        return SevenStar::from_index(index % 7 + 4);
    }

    Land TwentyEightStar::get_land() const {
        const int indices[28] = {4, 4, 4, 2, 2, 2, 7, 7, 7, 0, 0, 0, 0, 5, 5, 5, 6, 6, 6, 1, 1, 1, 8, 8, 8, 3, 3, 3};
        return Land::from_index(indices[index]);
    }

    Zone TwentyEightStar::get_zone() const {
        return Zone::from_index(index / 7);
    }

    Animal TwentyEightStar::get_animal() const {
        return Animal::from_index(index);
    }

    Luck TwentyEightStar::get_luck() const {
        const int indices[28] = {0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0};
        return Luck::from_index(indices[index]);
    }

    const double JulianDay::J2000 = 2451545;

    JulianDay JulianDay::from_julian_day(const double day) {
        return JulianDay(day);
    }

    JulianDay JulianDay::from_ymd_hms(const int year, const int month, const int day, const int hour, const int minute, const int second) {
        int y = year;
        int m = month;
        const double d = day + ((second * 1.0 / 60 + minute) / 60 + hour) / 24;
        int n = 0;
        const bool g = y * 372 + m * 31 + static_cast<int>(d) >= 588829;
        if (m <= 2) {
            m += 12;
            y--;
        }
        if (g) {
            n = static_cast<int>(y * 0.01);
            n = 2 - n + static_cast<int>(n * 0.25);
        }
        return from_julian_day(static_cast<int>(365.25 * (y + 4716)) + static_cast<int>(30.6001 * (m + 1)) + d + n - 1524.5);
    }

    double JulianDay::get_day() const {
        return day;
    }

    string JulianDay::get_name() const {
        return std::to_string(day);
    }

    JulianDay JulianDay::next(const int n) const {
        return from_julian_day(day + n);
    }

    Week JulianDay::get_week() const {
        return Week::from_index(static_cast<int>(floor(day + 0.5)) + 7000001);
    }

    double JulianDay::subtract(const JulianDay &target) const {
        return day - target.get_day();
    }

    SolarTime JulianDay::get_solar_time() const {
        int d = static_cast<int>(floor(day + 0.5));
        double f = day + 0.5 - d;

        if (d >= 2299161) {
            const int c = static_cast<int>((d - 1867216.25) / 36524.25);
            d += 1 + c - static_cast<int>(c * 0.25);
        }
        d += 1524;
        int y = static_cast<int>((d - 122.1) / 365.25);
        d -= static_cast<int>(365.25 * y);
        int m = static_cast<int>(d / 30.601);
        d -= static_cast<int>(30.601 * m);
        if (m > 13) {
            m -= 12;
        } else {
            y -= 1;
        }
        m -= 1;
        y -= 4715;
        f *= 24;
        const int hour = static_cast<int>(f);

        f -= hour;
        f *= 60;
        const int minute = static_cast<int>(f);

        f -= minute;
        f *= 60;
        const int second = static_cast<int>(round(f));
        if (second < 60) {
            return SolarTime::from_ymd_hms(y, m, d, hour, minute, second);
        }
        return SolarTime::from_ymd_hms(y, m, d, hour, minute, second - 60).next(60);
    }

    SolarDay JulianDay::get_solar_day() const {
        return get_solar_time().get_solar_day();
    }

    const vector<string> HeavenStem::NAMES = {
        "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"
    };

    HeavenStem HeavenStem::from_index(const int index) {
        return HeavenStem(index);
    }

    HeavenStem HeavenStem::from_name(const string &name) {
        return HeavenStem(name);
    }

    HeavenStem HeavenStem::next(const int n) const {
        return from_index(next_index(n));
    }

    Element HeavenStem::get_element() const {
        return Element::from_index(index / 2);
    }

    YinYang HeavenStem::get_yin_yang() const {
        return index % 2 == 0 ? YinYang::YANG : YinYang::YIN;
    }

    TenStar HeavenStem::get_ten_star(const HeavenStem &target) const {
        const int target_index = target.get_index();
        int offset = target_index - index;
        if (index % 2 != 0 && target_index % 2 == 0) {
            offset += 2;
        }
        return TenStar::from_index(offset);
    }

    Direction HeavenStem::get_direction() const {
        return get_element().get_direction();
    }

    Direction HeavenStem::get_joy_direction() const {
        const int indices[5] = {7, 5, 1, 8, 3};
        return Direction::from_index(indices[index % 5]);
    }

    Direction HeavenStem::get_yang_direction() const {
        const int indices[10] = {1, 1, 6, 5, 7, 0, 8, 7, 2, 3};
        return Direction::from_index(indices[index]);
    }

    Direction HeavenStem::get_yin_direction() const {
        const int indices[10] = {7, 0, 5, 6, 1, 1, 7, 8, 3, 2};
        return Direction::from_index(indices[index]);
    }

    Direction HeavenStem::get_wealth_direction() const {
        const int indices[5] = {7, 1, 0, 2, 8};
        return Direction::from_index(indices[index / 2]);
    }

    Direction HeavenStem::get_mascot_direction() const {
        const int indices[10] = {3, 3, 2, 2, 0, 8, 1, 1, 5, 6};
        return Direction::from_index(indices[index]);
    }

    PengZuHeavenStem HeavenStem::get_peng_zu_heaven_stem() const {
        return PengZuHeavenStem::from_index(index);
    }

    Terrain HeavenStem::get_terrain(const EarthBranch &earth_branch) const {
        const int earth_branch_index = earth_branch.get_index();
        const int indices[10] = {1, 6, 10, 9, 10, 9, 7, 0, 4, 3};
        return Terrain::from_index(indices[index] + (YinYang::YANG == get_yin_yang() ? earth_branch_index : -earth_branch_index));
    }

    HeavenStem HeavenStem::get_combine() const {
        return next(5);
    }

    optional<Element> HeavenStem::combine(const HeavenStem &target) const {
        if (get_combine().equals(target)) {
            return Element::from_index(index + 2);
        }
        return nullopt;
    }

    HeavenStem HideHeavenStem::get_heaven_stem() const {
        return heaven_stem;
    }

    HideHeavenStemType HideHeavenStem::get_type() const {
        return type;
    }

    string HideHeavenStem::get_name() const {
        return heaven_stem.get_name();
    }

    HideHeavenStem HideHeavenStemDay::get_hide_heaven_stem() const {
        return hide_heaven_stem;
    }

    int HideHeavenStemDay::get_day_index() const {
        return day_index;
    }

    string HideHeavenStemDay::get_name() const {
        const HeavenStem heaven_stem = get_hide_heaven_stem().get_heaven_stem();
        return heaven_stem.get_name() + heaven_stem.get_element().get_name();
    }

    string HideHeavenStemDay::to_string() const {
        return get_name() + "第" + std::to_string(day_index + 1) + "天";
    }

    bool HeavenStem::equals(const HeavenStem &other) const {
        return to_string() == other.to_string();
    }

    const vector<string> EarthBranch::NAMES = {
        "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"
    };

    EarthBranch EarthBranch::from_index(const int index) {
        return EarthBranch(index);
    }

    EarthBranch EarthBranch::from_name(const string &name) {
        return EarthBranch(name);
    }

    EarthBranch EarthBranch::next(const int n) const {
        return from_index(next_index(n));
    }

    Element EarthBranch::get_element() const {
        const int indices[12] = {4, 2, 0, 0, 2, 1, 1, 2, 3, 3, 2, 4};
        return Element::from_index(indices[index]);
    }

    YinYang EarthBranch::get_yin_yang() const {
        return index % 2 == 0 ? YinYang::YANG : YinYang::YIN;
    }

    HeavenStem EarthBranch::get_hide_heaven_stem_main() const {
        const int indices[12] = {9, 5, 0, 1, 4, 2, 3, 5, 6, 7, 4, 8};
        return HeavenStem::from_index(indices[index]);
    }

    optional<HeavenStem> EarthBranch::get_hide_heaven_stem_middle() const {
        constexpr int indices[12] = {-1, 9, 2, -1, 1, 6, 5, 3, 8, -1, 7, 0};
        const int n = indices[index];
        if (n == -1) {
            return nullopt;
        }
        return HeavenStem::from_index(n);
    }

    optional<HeavenStem> EarthBranch::get_hide_heaven_stem_residual() const {
        constexpr int indices[12] = {-1, 7, 4, -1, 9, 4, -1, 1, 4, -1, 3, -1};
        const int n = indices[index];
        if (n == -1) {
            return nullopt;
        }
        return HeavenStem::from_index(n);
    }

    vector<HideHeavenStem> EarthBranch::get_hide_heaven_stems() const {
        auto l = vector<HideHeavenStem>();
        l.emplace_back(get_hide_heaven_stem_main(), HideHeavenStemType::MAIN);
        optional<HeavenStem> o = get_hide_heaven_stem_middle();
        if (o.has_value()) {
            l.emplace_back(o.value(), HideHeavenStemType::MIDDLE);
        }
        o = get_hide_heaven_stem_residual();
        if (o.has_value()) {
            l.emplace_back(o.value(), HideHeavenStemType::RESIDUAL);
        }
        return l;
    }

    Zodiac EarthBranch::get_zodiac() const {
        return Zodiac::from_index(index);
    }

    Direction EarthBranch::get_direction() const {
        const int indices[12] = {0, 4, 2, 2, 4, 8, 8, 4, 6, 6, 4, 0};
        return Direction::from_index(indices[index]);
    }

    Direction EarthBranch::get_ominous() const {
        constexpr int indices[4] = {8, 2, 0, 6};
        return Direction::from_index(indices[index % 4]);
    }

    PengZuEarthBranch EarthBranch::get_peng_zu_earth_branch() const {
        return PengZuEarthBranch::from_index(index);
    }

    EarthBranch EarthBranch::get_opposite() const {
        return next(6);
    }

    EarthBranch EarthBranch::get_combine() const {
        return from_index(1 - index);
    }

    EarthBranch EarthBranch::get_harm() const {
        return from_index(19 - index);
    }

    optional<Element> EarthBranch::combine(const EarthBranch &target) const {
        if (get_combine().equals(&target)) {
            const int indices[12] = {2, 2, 0, 1, 3, 4, 2, 2, 4, 3, 1, 0};
            return Element::from_index(indices[index]);
        }
        return nullopt;
    }

    const vector<string> SixtyCycle::NAMES = {
        "甲子", "乙丑", "丙寅", "丁卯", "戊辰", "己巳", "庚午", "辛未", "壬申", "癸酉", "甲戌", "乙亥", "丙子", "丁丑", "戊寅", "己卯", "庚辰", "辛巳", "壬午", "癸未", "甲申", "乙酉", "丙戌", "丁亥", "戊子", "己丑", "庚寅", "辛卯", "壬辰", "癸巳", "甲午", "乙未", "丙申", "丁酉", "戊戌", "己亥", "庚子", "辛丑", "壬寅", "癸卯", "甲辰", "乙巳", "丙午", "丁未", "戊申", "己酉", "庚戌", "辛亥", "壬子", "癸丑", "甲寅", "乙卯", "丙辰", "丁巳", "戊午", "己未", "庚申", "辛酉", "壬戌", "癸亥"
    };

    SixtyCycle SixtyCycle::from_index(const int index) {
        return SixtyCycle(index);
    }

    SixtyCycle SixtyCycle::from_name(const string &name) {
        return SixtyCycle(name);
    }

    SixtyCycle SixtyCycle::next(const int n) const {
        return from_index(next_index(n));
    }

    HeavenStem SixtyCycle::get_heaven_stem() const {
        return HeavenStem::from_index(index % static_cast<int>(HeavenStem::NAMES.size()));
    }

    EarthBranch SixtyCycle::get_earth_branch() const {
        return EarthBranch::from_index(index % static_cast<int>(EarthBranch::NAMES.size()));
    }

    Sound SixtyCycle::get_sound() const {
        return Sound::from_index(index / 2);
    }

    PengZu SixtyCycle::get_peng_zu() const {
        return PengZu::from_sixty_cycle(*this);
    }

    Ten SixtyCycle::get_ten() const {
        return Ten::from_index((get_heaven_stem().get_index() - get_earth_branch().get_index()) / 2);
    }

    vector<EarthBranch> SixtyCycle::get_extra_earth_branches() const {
        auto l = vector<EarthBranch>();
        const EarthBranch e = EarthBranch::from_index(10 + get_earth_branch().get_index() - get_heaven_stem().get_index());
        l.push_back(e);
        l.push_back(e.next(1));
        return l;
    }

    SixtyCycleYear SixtyCycleYear::from_year(int year) {
        return SixtyCycleYear(year);
    }

    int SixtyCycleYear::get_year() const {
        return year;
    }

    SixtyCycle SixtyCycleYear::get_sixty_cycle() const {
        return SixtyCycle::from_index(year - 4);
    }

    string SixtyCycleYear::get_name() const {
        return get_sixty_cycle().to_string() + "年";
    }

    Twenty SixtyCycleYear::get_twenty() const {
        return Twenty::from_index(static_cast<int>(floor((year - 1864) / 20.0)));
    }

    NineStar SixtyCycleYear::get_nine_star() const {
        return NineStar::from_index(63 + get_twenty().get_sixty().get_index() * 3 - get_sixty_cycle().get_index());
    }

    Direction SixtyCycleYear::get_jupiter_direction() const {
        const int indices[] = {0, 7, 7, 2, 3, 3, 8, 1, 1, 6, 0, 0};
        return Direction::from_index(indices[get_sixty_cycle().get_earth_branch().get_index()]);
    }

    SixtyCycleYear SixtyCycleYear::next(int n) const {
        return from_year(year + n);
    }

    SixtyCycleMonth SixtyCycleYear::get_first_month() const {
        const HeavenStem h = HeavenStem::from_index((get_sixty_cycle().get_heaven_stem().get_index() + 1) * 2);
        return SixtyCycleMonth(*this, SixtyCycle::from_name(h.get_name() + "寅"));
    }

    vector<SixtyCycleMonth> SixtyCycleYear::get_months() const {
        auto l = vector<SixtyCycleMonth>();
        const SixtyCycleMonth m = get_first_month();
        l.push_back(m);
        for (int i = 1; i < 12; i++) {
            l.push_back(m.next(i));
        }
        return l;
    }

    SixtyCycleMonth SixtyCycleMonth::from_index(int year, int index) {
        return SixtyCycleYear::from_year(year).get_first_month().next(index);
    }

    SixtyCycleYear SixtyCycleMonth::get_sixty_cycle_year() const {
        return year;
    }

    SixtyCycle SixtyCycleMonth::get_year() const {
        return year.get_sixty_cycle();
    }

    SixtyCycle SixtyCycleMonth::get_sixty_cycle() const {
        return month;
    }

    string SixtyCycleMonth::get_name() const {
        return month.to_string() + "月";
    }

    string SixtyCycleMonth::to_string() const {
        return year.to_string() + get_name();
    }

    SixtyCycleMonth SixtyCycleMonth::next(const int n) const {
        return SixtyCycleMonth(SixtyCycleYear::from_year((year.get_year() * 12 + get_index_in_year() + n) / 12), month.next(n));
    }

    int SixtyCycleMonth::get_index_in_year() const {
        return month.get_earth_branch().next(-2).get_index();
    }

    NineStar SixtyCycleMonth::get_nine_star() const {
        int index = month.get_earth_branch().get_index();
        if (index < 2) {
            index += 3;
        }
        return NineStar::from_index(27 - get_year().get_earth_branch().get_index() % 3 * 3 - index);
    }

    Direction SixtyCycleMonth::get_jupiter_direction() const {
        constexpr int indices[] = {7, -1, 1, 3};
        const int n = indices[month.get_earth_branch().next(-2).get_index() % 4];
        if (n == -1) {
            return month.get_heaven_stem().get_direction();
        }
        return Direction::from_index(n);
    }

    SixtyCycleDay SixtyCycleMonth::get_first_day() const {
        return SixtyCycleDay::from_solar_day(SolarTerm::from_index(year.get_year(), 3 + get_index_in_year() * 2).get_solar_day());
    }

    vector<SixtyCycleDay> SixtyCycleMonth::get_days() const {
        auto l = vector<SixtyCycleDay>();
        SixtyCycleDay d = get_first_day();
        while (d.get_sixty_cycle_month().equals(this)) {
            l.push_back(d);
            d = d.next(1);
        }
        return l;
    }

    SixtyCycleDay &SixtyCycleDay::operator=(const SixtyCycleDay &other) {
        solar_day = other.solar_day;
        month = other.month;
        day = other.day;
        return *this;
    }

    SixtyCycleDay SixtyCycleDay::from_solar_day(const SolarDay &solar_day) {
        return SixtyCycleDay(solar_day);
    }

    SolarDay SixtyCycleDay::get_solar_day() const {
        return solar_day;
    }

    SixtyCycleMonth SixtyCycleDay::get_sixty_cycle_month() const {
        return month.value();
    }

    SixtyCycle SixtyCycleDay::get_year() const {
        return get_sixty_cycle_month().get_year();
    }

    SixtyCycle SixtyCycleDay::get_month() const {
        return month.value().get_sixty_cycle();
    }

    SixtyCycle SixtyCycleDay::get_sixty_cycle() const {
        return day.value();
    }

    string SixtyCycleDay::get_name() const {
        return get_sixty_cycle().to_string() + "日";
    }

    string SixtyCycleDay::to_string() const {
        return month.value().to_string() + get_name();
    }

    Duty SixtyCycleDay::get_duty() const {
        return Duty::from_index(get_sixty_cycle().get_earth_branch().get_index() - get_month().get_earth_branch().get_index());
    }

    TwelveStar SixtyCycleDay::get_twelve_star() const {
        return TwelveStar::from_index(get_sixty_cycle().get_earth_branch().get_index() + (8 - get_month().get_earth_branch().get_index() % 6) * 2);
    }

    NineStar SixtyCycleDay::get_nine_star() const {
        const SolarTerm dong_zhi = SolarTerm::from_index(solar_day.get_year(), 0);
        const SolarDay dong_zhi_solar = dong_zhi.get_solar_day();
        const SolarDay xia_zhi_solar = dong_zhi.next(12).get_solar_day();
        const SolarDay dong_zhi_solar2 = dong_zhi.next(24).get_solar_day();
        const int dong_zhi_index = dong_zhi_solar.get_lunar_day().get_sixty_cycle().get_index();
        const int xia_zhi_index = xia_zhi_solar.get_lunar_day().get_sixty_cycle().get_index();
        const int dong_zhi_index2 = dong_zhi_solar2.get_lunar_day().get_sixty_cycle().get_index();
        const SolarDay solar_shun_bai = dong_zhi_solar.next(dong_zhi_index > 29 ? 60 - dong_zhi_index : -dong_zhi_index);
        const SolarDay solar_shun_bai2 = dong_zhi_solar2.next(dong_zhi_index2 > 29 ? 60 - dong_zhi_index2 : -dong_zhi_index2);
        const SolarDay solar_ni_zi = xia_zhi_solar.next(xia_zhi_index > 29 ? 60 - xia_zhi_index : -xia_zhi_index);
        int offset = 0;
        if (!solar_day.is_before(solar_shun_bai) && solar_day.is_before(solar_ni_zi)) {
            offset = solar_day.subtract(solar_shun_bai);
        } else if (!solar_day.is_before(solar_ni_zi) && solar_day.is_before(solar_shun_bai2)) {
            offset = 8 - solar_day.subtract(solar_ni_zi);
        } else if (!solar_day.is_before(solar_shun_bai2)) {
            offset = solar_day.subtract(solar_shun_bai2);
        } else if (solar_day.is_before(solar_shun_bai)) {
            offset = 8 + solar_shun_bai.subtract(solar_day);
        }
        return NineStar::from_index(offset);
    }

    Direction SixtyCycleDay::get_jupiter_direction() const {
        if (const int index = get_sixty_cycle().get_index(); index % 12 < 6) {
            return Element::from_index(index / 12).get_direction();
        }
        return get_sixty_cycle_month().get_sixty_cycle_year().get_jupiter_direction();
    }

    FetusDay SixtyCycleDay::get_fetus_day() const {
        return FetusDay::from_sixty_cycle_day(*this);
    }

    TwentyEightStar SixtyCycleDay::get_twenty_eight_star() const {
        const int indices[] = {10, 18, 26, 6, 14, 22, 2};
        return TwentyEightStar::from_index(indices[solar_day.get_week().get_index()]).next(-7 * get_sixty_cycle().get_earth_branch().get_index());
    }

    vector<God> SixtyCycleDay::get_gods() const {
        return God::get_day_gods(get_month(), get_sixty_cycle());
    }

    vector<Taboo> SixtyCycleDay::get_recommends() const {
        return Taboo::get_day_recommends(get_month(), get_sixty_cycle());
    }

    vector<Taboo> SixtyCycleDay::get_avoids() const {
        return Taboo::get_day_avoids(get_month(), get_sixty_cycle());
    }

    SixtyCycleDay SixtyCycleDay::next(int n) const {
        return from_solar_day(solar_day.next(n));
    }

    vector<SixtyCycleHour> SixtyCycleDay::get_hours() const {
        auto l = vector<SixtyCycleHour>();
        const SolarDay d = solar_day.next(-1);
        SixtyCycleHour h = SixtyCycleHour::from_solar_time(SolarTime::from_ymd_hms(d.get_year(), d.get_month(), d.get_day(), 23, 0, 0));
        l.push_back(h);
        for (int i = 0; i < 11; i++) {
            h = h.next(7200);
            l.push_back(h);
        }
        return l;
    }

    ThreePillars SixtyCycleDay::get_three_pillars() const {
        return ThreePillars(get_year(), get_month(), get_sixty_cycle());
    }

    SixtyCycleHour SixtyCycleHour::from_solar_time(const SolarTime &solar_time) {
        return SixtyCycleHour(solar_time);
    }

    SixtyCycle SixtyCycleHour::get_year() const {
        return get_sixty_cycle_day().get_year();
    }

    SixtyCycle SixtyCycleHour::get_month() const {
        return get_sixty_cycle_day().get_month();
    }

    SixtyCycle SixtyCycleHour::get_day() const {
        return get_sixty_cycle_day().get_sixty_cycle();
    }

    SixtyCycle SixtyCycleHour::get_sixty_cycle() const {
        return hour.value();
    }

    SixtyCycleDay SixtyCycleHour::get_sixty_cycle_day() const {
        return day.value();
    }

    SolarTime SixtyCycleHour::get_solar_time() const {
        return solar_time;
    }

    string SixtyCycleHour::get_name() const {
        return get_sixty_cycle().to_string() + "时";
    }

    string SixtyCycleHour::to_string() const {
        return get_sixty_cycle_day().to_string() + get_name();
    }

    int SixtyCycleHour::get_index_in_day() const {
        int h = solar_time.get_hour();
        if (h == 23) {
            return 0;
        }
        return (h + 1) / 2;
    }

    NineStar SixtyCycleHour::get_nine_star() const {
        const SolarDay solar = solar_time.get_solar_day();
        const SolarTerm dong_zhi = SolarTerm::from_index(solar.get_year(), 0);
        const SolarTerm xia_zhi = dong_zhi.next(12);
        const int earth_branch_index = get_index_in_day() % 12;
        constexpr int indices[] = {8, 5, 2};
        int index = indices[get_day().get_earth_branch().get_index() % 3];
        if (!solar.is_before(dong_zhi.get_julian_day().get_solar_day()) && solar.is_before(xia_zhi.get_julian_day().get_solar_day())) {
            index = 8 + earth_branch_index - index;
        } else {
            index -= earth_branch_index;
        }
        return NineStar::from_index(index);
    }

    TwelveStar SixtyCycleHour::get_twelve_star() const {
        return TwelveStar::from_index(get_sixty_cycle().get_earth_branch().get_index() + (8 - get_day().get_earth_branch().get_index() % 6) * 2);
    }

    vector<Taboo> SixtyCycleHour::get_recommends() const {
        return Taboo::get_hour_recommends(get_day(), get_sixty_cycle());
    }

    vector<Taboo> SixtyCycleHour::get_avoids() const {
        return Taboo::get_hour_avoids(get_day(), get_sixty_cycle());
    }

    SixtyCycleHour SixtyCycleHour::next(const int n) const {
        return from_solar_time(solar_time.next(n));
    }

    EightChar SixtyCycleHour::get_eight_char() const {
        return EightChar(get_year(), get_month(), get_day(), get_sixty_cycle());
    }

    vector<vector<int>> LunarYear::LEAP = vector<vector<int>>();

    LunarYear LunarYear::from_year(const int year) {
        return LunarYear(year);
    }

    LunarYear& LunarYear::operator=(const LunarYear &other) {
        year = other.year;
        return *this;
    }

    int LunarYear::get_year() const {
        return year;
    }

    SixtyCycle LunarYear::get_sixty_cycle() const {
        return SixtyCycle::from_index(year - 4);
    }

    int LunarYear::get_day_count() const {
        int n = 0;
        for (const LunarMonth& m: get_months()) {
            n += m.get_day_count();
        }
        return n;
    }

    int LunarYear::get_month_count() const {
        if (get_leap_month() < 1) {
            return 12;
        }
        return 13;
    }

    string LunarYear::get_name() const {
        return "农历" + get_sixty_cycle().to_string() + "年";
    }

    Twenty LunarYear::get_twenty() const {
        return Twenty::from_index(static_cast<int>(floor((year - 1864) / 20.0)));
    }

    NineStar LunarYear::get_nine_star() const {
        return NineStar::from_index(63 + get_twenty().get_sixty().get_index() * 3 - get_sixty_cycle().get_index());
    }

    Direction LunarYear::get_jupiter_direction() const {
        const int indices[] = {0, 7, 7, 2, 3, 3, 8, 1, 1, 6, 0, 0};
        return Direction::from_index(indices[get_sixty_cycle().get_earth_branch().get_index()]);
    }

    LunarYear LunarYear::next(const int n) const {
        return from_year(year + n);
    }

    int LunarYear::get_leap_month() const {
        if (year == -1) {
            return 11;
        }
        for (int i = 0; i < 12; i++) {
            if (vector<int> l = LEAP[i]; find(l.begin(), l.end(), year) != l.end()) {
                return i + 1;
            }
        }
        return 0;
    }

    KitchenGodSteed LunarYear::get_kitchen_god_steed() const {
        return KitchenGodSteed::from_lunar_year(year);
    }

    LunarMonth LunarYear::get_first_month() const {
        return LunarMonth::from_ym(year, 1);
    }

    vector<LunarMonth> LunarYear::get_months() const {
        auto l = vector<LunarMonth>();
        LunarMonth m = get_first_month();
        while (m.get_year() == year) {
            l.push_back(m);
            m = m.next(1);
        }
        return l;
    }

    const vector<string> LunarSeason::NAMES = {"孟春", "仲春", "季春", "孟夏", "仲夏", "季夏", "孟秋", "仲秋", "季秋", "孟冬", "仲冬", "季冬"};

    LunarSeason LunarSeason::from_index(const int index) {
        return LunarSeason(index);
    }

    LunarSeason LunarSeason::from_name(const string &name) {
        return LunarSeason(name);
    }

    LunarSeason LunarSeason::next(const int n) const {
        return from_index(next_index(n));
    }

    const vector<string> LunarMonth::NAMES = {"正月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"};

    LunarMonth LunarMonth::from_ym(const int year, const int month) {
        return LunarMonth(year, month);
    }

    LunarMonth &LunarMonth::operator=(const LunarMonth &other) {
        year = other.year;
        month = other.month;
        leap = other.leap;
        day_count = other.day_count;
        index_in_year = other.index_in_year;
        first_julian_day = other.first_julian_day;
        return *this;
    }

    LunarYear LunarMonth::get_lunar_year() const {
        return year;
    }

    int LunarMonth::get_year() const {
        return year.get_year();
    }

    int LunarMonth::get_month() const {
        return month;
    }

    int LunarMonth::get_month_with_leap() const {
        if (leap) {
            return -month;
        }
        return month;
    }

    int LunarMonth::get_day_count() const {
        return day_count;
    }

    int LunarMonth::get_index_in_year() const {
        return index_in_year;
    }

    LunarSeason LunarMonth::get_season() const {
        return LunarSeason::from_index(month - 1);
    }

    JulianDay LunarMonth::get_first_julian_day() const {
        return first_julian_day;
    }

    bool LunarMonth::is_leap() const {
        return leap;
    }

    int LunarMonth::get_week_count(int start) const {
        return static_cast<int>(ceil((index_of(first_julian_day.get_week().get_index() - start, 7) + get_day_count()) / 7.0));
    }

    string LunarMonth::get_name() const {
        string s = NAMES[month - 1];
        if (leap) {
            return "闰" + s;
        }
        return s;
    }

    string LunarMonth::to_string() const {
        return year.to_string() + get_name();
    }

    LunarMonth LunarMonth::next(const int n) const {
        if (n == 0) {
            return from_ym(get_year(), get_month_with_leap());
        }
        int m = index_in_year + 1 + n;
        LunarYear y = year;
        if (n > 0) {
            int month_count = y.get_month_count();
            while (m > month_count) {
                m -= month_count;
                y = y.next(1);
                month_count = y.get_month_count();
            }
        } else {
            while (m <= 0) {
                y = y.next(-1);
                m += y.get_month_count();
            }
        }
        bool leap = false;
        if (const int leap_month = y.get_leap_month(); leap_month > 0) {
            if (m == leap_month + 1) {
                leap = true;
            }
            if (m > leap_month) {
                m--;
            }
        }
        if (leap) {
            m = -m;
        }
        return from_ym(y.get_year(), m);
    }

    SixtyCycle LunarMonth::get_sixty_cycle() const {
        return SixtyCycle::from_name(HeavenStem::from_index(year.get_sixty_cycle().get_heaven_stem().get_index() * 2 + month + 1).get_name() + EarthBranch::from_index(month + 1).get_name());
    }

    NineStar LunarMonth::get_nine_star() const {
        int index = get_sixty_cycle().get_earth_branch().get_index();
        if (index < 2) {
            index += 3;
        }
        return NineStar::from_index(27 - year.get_sixty_cycle().get_earth_branch().get_index() % 3 * 3 - index);
    }

    Direction LunarMonth::get_jupiter_direction() const {
        const SixtyCycle sixty_cycle = get_sixty_cycle();
        constexpr int indices[] = {7, -1, 1, 3};
        if (const int n = indices[sixty_cycle.get_earth_branch().next(-2).get_index() % 4]; n != -1) {
            return Direction::from_index(n);
        }
        return sixty_cycle.get_heaven_stem().get_direction();
    }

    optional<FetusMonth> LunarMonth::get_fetus() const {
        return FetusMonth::from_lunar_month(*this);
    }

    MinorRen LunarMonth::get_minor_ren() const {
        return MinorRen::from_index((month - 1) % 6);
    }

    const vector<string> LunarWeek::NAMES = {
        "第一周", "第二周", "第三周", "第四周", "第五周", "第六周"
    };

    LunarWeek LunarWeek::from_ym(const int year, const int month, const int index, const int start) {
        return LunarWeek(year, month, index, start);
    }

    LunarMonth LunarWeek::get_lunar_month() const {
        return month;
    }

    int LunarWeek::get_year() const {
        return month.get_year();
    }

    int LunarWeek::get_month() const {
        return month.get_month();
    }

    int LunarWeek::get_index() const {
        return index;
    }

    Week LunarWeek::get_start() const {
        return start;
    }

    string LunarWeek::get_name() const {
        return NAMES[index];
    }

    string LunarWeek::to_string() const {
        return month.to_string() + get_name();
    }

    LunarWeek LunarWeek::next(const int n) const {
        const int start_index = start.get_index();
        int d = index;
        LunarMonth m = month;
        if (n > 0) {
            d += n;
            int weekCount = m.get_week_count(start_index);
            while (d >= weekCount) {
                d -= weekCount;
                m = m.next(1);
                if (!LunarDay::from_ymd(m.get_year(), m.get_month_with_leap(), 1).get_week().equals(&start)) {
                    d += 1;
                }
                weekCount = m.get_week_count(start_index);
            }
        } else if (n < 0) {
            d += n;
            while (d < 0) {
                if (!LunarDay::from_ymd(m.get_year(), m.get_month_with_leap(), 1).get_week().equals(&start)) {
                    d -= 1;
                }
                m = m.next(-1);
                d += m.get_week_count(start_index);
            }
        }
        return from_ym(m.get_year(), m.get_month_with_leap(), d, start_index);
    }

    LunarDay LunarWeek::get_first_day() const {
        const LunarDay first_day = LunarDay::from_ymd(get_year(), get_month(), 1);
        return first_day.next(index * 7 - index_of(first_day.get_week().get_index() - start.get_index(), 7));
    }

    vector<LunarDay> LunarWeek::get_days() const {
        auto l = vector<LunarDay>();
        const LunarDay d = get_first_day();
        l.push_back(d);
        for (int i = 1; i < 7; i++) {
            l.push_back(d.next(i));
        }
        return l;
    }

    bool LunarWeek::equals(const LunarWeek &other) const {
        return get_first_day().equals(other.get_first_day());
    }

    const vector<string> LunarDay::NAMES = {
        "初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十", "十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十", "廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十"
    };

    LunarDay LunarDay::from_ymd(const int year, const int month, const int day) {
        return LunarDay(year, month, day);
    }

    LunarMonth LunarDay::get_lunar_month() const {
        return month;
    }

    int LunarDay::get_year() const {
        return month.get_year();
    }

    int LunarDay::get_month() const {
        return month.get_month_with_leap();
    }

    int LunarDay::get_day() const {
        return day;
    }

    string LunarDay::get_name() const {
        return NAMES[day - 1];
    }

    string LunarDay::to_string() const {
        return month.to_string() + get_name();
    }

    LunarDay LunarDay::next(const int n) const {
        return get_solar_day().next(n).get_lunar_day();
    }

    bool LunarDay::equals(const LunarDay& other) const {
        return to_string() == other.to_string();
    }

    bool LunarDay::is_before(const LunarDay& other) const {
        const int a_year = get_year();
        if (const int b_year = other.get_year(); a_year != b_year) {
            return a_year < b_year;
        }
        const int a_month = get_month();
        if (const int b_month = other.get_month(); a_month != b_month) {
            return abs(a_month) < abs(b_month);
        }
        return day < other.get_day();
    }

    bool LunarDay::is_after(const LunarDay& other) const {
        const int a_year = get_year();
        if (const int b_year = other.get_year(); a_year != b_year) {
            return a_year > b_year;
        }
        const int a_month = get_month();
        if (const int b_month = other.get_month(); a_month != b_month) {
            return abs(a_month) >= abs(b_month);
        }
        return day > other.get_day();
    }

    Week LunarDay::get_week() const {
        return get_solar_day().get_week();
    }

    SixtyCycle LunarDay::get_sixty_cycle() const {
        const int offset = static_cast<int>(month.get_first_julian_day().next(day - 12).get_day());
        return SixtyCycle::from_name(HeavenStem::from_index(offset).get_name() + EarthBranch::from_index(offset).get_name());
    }

    Duty LunarDay::get_duty() const {
        return get_sixty_cycle_day().get_duty();
    }

    TwelveStar LunarDay::get_twelve_star() const {
        return get_sixty_cycle_day().get_twelve_star();
    }

    NineStar LunarDay::get_nine_star() const {
        const SolarDay d = get_solar_day();
        const SolarTerm dong_zhi = SolarTerm::from_index(d.get_year(), 0);
        const SolarDay dong_zhi_solar = dong_zhi.get_solar_day();
        const SolarDay xia_zhi_solar = dong_zhi.next(12).get_solar_day();
        const SolarDay dong_zhi_solar2 = dong_zhi.next(24).get_solar_day();
        const int dong_zhi_index = dong_zhi_solar.get_lunar_day().get_sixty_cycle().get_index();
        const int xia_zhi_index = xia_zhi_solar.get_lunar_day().get_sixty_cycle().get_index();
        const int dong_zhi_index2 = dong_zhi_solar2.get_lunar_day().get_sixty_cycle().get_index();
        const SolarDay solar_shun_bai = dong_zhi_solar.next(dong_zhi_index > 29 ? 60 - dong_zhi_index : -dong_zhi_index);
        const SolarDay solar_shun_bai2 = dong_zhi_solar2.next(dong_zhi_index2 > 29 ? 60 - dong_zhi_index2 : -dong_zhi_index2);
        const SolarDay solar_ni_zi = xia_zhi_solar.next(xia_zhi_index > 29 ? 60 - xia_zhi_index : -xia_zhi_index);
        int offset = 0;
        if (!d.is_before(solar_shun_bai) && d.is_before(solar_ni_zi)) {
            offset = d.subtract(solar_shun_bai);
        } else if (!d.is_before(solar_ni_zi) && d.is_before(solar_shun_bai2)) {
            offset = 8 - d.subtract(solar_ni_zi);
        } else if (!d.is_before(solar_shun_bai2)) {
            offset = d.subtract(solar_shun_bai2);
        } else if (d.is_before(solar_shun_bai)) {
            offset = 8 + solar_shun_bai.subtract(d);
        }
        return NineStar::from_index(offset);
    }

    Direction LunarDay::get_jupiter_direction() const {
        if (const int index = get_sixty_cycle().get_index(); index % 12 < 6) {
            return Element::from_index(index / 12).get_direction();
        }
        return month.get_lunar_year().get_jupiter_direction();
    }

    FetusDay LunarDay::get_fetus_day() const {
        return FetusDay::from_lunar_day(*this);
    }

    PhaseDay LunarDay::get_phase_day() const {
        const SolarDay today = get_solar_day();
        const LunarMonth m = month.next(1);
        Phase p = Phase::from_index(m.get_year(), m.get_month_with_leap(), 0);
        SolarDay d = p.get_solar_day();
        while (d.is_after(today)) {
            p = p.next(-1);
            d = p.get_solar_day();
        }
        return PhaseDay(p, today.subtract(d));
    }

    Phase LunarDay::get_phase() const {
        return get_phase_day().get_phase();
    }

    SixStar LunarDay::get_six_star() const {
        return SixStar::from_index((month.get_month() + day - 2) % 6);
    }

    SolarDay LunarDay::get_solar_day() const {
        return month.get_first_julian_day().next(day - 1).get_solar_day();
    }

    SixtyCycleDay LunarDay::get_sixty_cycle_day() const {
        return get_solar_day().get_sixty_cycle_day();
    }

    TwentyEightStar LunarDay::get_twenty_eight_star() const {
        const int indices[] = {10, 18, 26, 6, 14, 22, 2};
        return TwentyEightStar::from_index(indices[get_solar_day().get_week().get_index()]).next(-7 * get_sixty_cycle().get_earth_branch().get_index());
    }

    vector<God> LunarDay::get_gods() const {
        return get_sixty_cycle_day().get_gods();
    }

    vector<Taboo> LunarDay::get_recommends() const {
        return get_sixty_cycle_day().get_recommends();
    }

    vector<Taboo> LunarDay::get_avoids() const {
        return get_sixty_cycle_day().get_avoids();
    }

    MinorRen LunarDay::get_minor_ren() const {
        return get_lunar_month().get_minor_ren().next(day - 1);
    }

    optional<LunarFestival> LunarDay::get_festival() const {
        return LunarFestival::from_ymd(get_year(), get_month(), day);
    }

    ThreePillars LunarDay::get_three_pillars() const {
        return get_sixty_cycle_day().get_three_pillars();
    }

    LunarHour LunarHour::from_ymd_hms(const int year, const int month, int day, int hour, const int minute, int second) {
        return LunarHour(year, month, day, hour, minute, second);
    }

    LunarDay LunarHour::get_lunar_day() const {
        return day;
    }

    int LunarHour::get_year() const {
        return day.get_year();
    }

    int LunarHour::get_month() const {
        return day.get_month();
    }

    int LunarHour::get_day() const {
        return day.get_day();
    }

    int LunarHour::get_hour() const {
        return hour;
    }

    int LunarHour::get_minute() const {
        return minute;
    }

    int LunarHour::get_second() const {
        return second;
    }

    string LunarHour::get_name() const {
        return EarthBranch::from_index(get_index_in_day()).get_name() + "时";
    }

    string LunarHour::to_string() const {
        return day.to_string() + get_sixty_cycle().get_name() + "时";
    }

    int LunarHour::get_index_in_day() const {
        return (hour + 1) / 2;
    }

    LunarHour LunarHour::next(int n) const {
        if (n == 0) {
            return from_ymd_hms(get_year(), get_month(), get_day(), hour, minute, second);
        }
        const int h = hour + n * 2;
        int diff = 1;
        if (h < 0) {
            diff = -1;
        }
        int hour = abs(h);
        int days = hour / 24 * diff;
        hour = hour % 24 * diff;
        if (hour < 0) {
            hour += 24;
            days--;
        }
        const LunarDay d = day.next(days);
        return from_ymd_hms(d.get_year(), d.get_month(), d.get_day(), hour, minute, second);
    }

    bool LunarHour::is_before(const LunarHour &other) const {
        if (!day.equals(other.get_lunar_day())) {
            return day.is_before(other.get_lunar_day());
        }
        if (hour != other.get_hour()) {
            return hour < other.get_hour();
        }
        if (minute != other.get_minute()) {
            return minute < other.get_minute();
        }
        return second < other.get_second();
    }

    bool LunarHour::is_after(const LunarHour &other) const {
        if (!day.equals(other.get_lunar_day())) {
            return day.is_after(other.get_lunar_day());
        }
        if (hour != other.get_hour()) {
            return hour > other.get_hour();
        }
        if (minute != other.get_minute()) {
            return minute > other.get_minute();
        }
        return second > other.get_second();
    }

    SixtyCycle LunarHour::get_sixty_cycle() const {
        const int earth_branch_index = get_index_in_day() % 12;
        SixtyCycle d = day.get_sixty_cycle();
        if (hour >= 23) {
            d = d.next(1);
        }
        return SixtyCycle::from_name(HeavenStem::from_index(d.get_heaven_stem().get_index() % 5 * 2 + earth_branch_index).get_name() + EarthBranch::from_index(earth_branch_index).get_name());
    }

    TwelveStar LunarHour::get_twelve_star() const {
        return TwelveStar::from_index(get_sixty_cycle().get_earth_branch().get_index() + (8 - get_sixty_cycle_hour().get_day().get_earth_branch().get_index() % 6) * 2);
    }

    NineStar LunarHour::get_nine_star() const {
        const SolarDay solar = day.get_solar_day();
        const SolarTerm dong_zhi = SolarTerm::from_index(solar.get_year(), 0);
        const int earth_branch_index = get_index_in_day() % 12;
        constexpr int indices[] = {8, 5, 2};
        int index = indices[day.get_sixty_cycle().get_earth_branch().get_index() % 3];
        if (!solar.is_before(dong_zhi.get_julian_day().get_solar_day()) && solar.is_before(dong_zhi.next(12).get_julian_day().get_solar_day())) {
            index = 8 + earth_branch_index - index;
        } else {
            index -= earth_branch_index;
        }
        return NineStar::from_index(index);
    }

    SolarTime LunarHour::get_solar_time() const {
        const SolarDay d = day.get_solar_day();
        return SolarTime::from_ymd_hms(d.get_year(), d.get_month(), d.get_day(), hour, minute, second);
    }

    SixtyCycleHour LunarHour::get_sixty_cycle_hour() const {
        return get_solar_time().get_sixty_cycle_hour();
    }

    vector<Taboo> LunarHour::get_recommends() const {
        return Taboo::get_hour_recommends(get_sixty_cycle_hour().get_day(), get_sixty_cycle());
    }

    vector<Taboo> LunarHour::get_avoids() const {
        return Taboo::get_hour_avoids(get_sixty_cycle_hour().get_day(), get_sixty_cycle());
    }

    MinorRen LunarHour::get_minor_ren() const {
        return get_lunar_day().get_minor_ren().next(get_index_in_day());
    }

    EightChar LunarHour::get_eight_char() const {
        return provider->get_eight_char(*this);
    }

    EightCharProvider* LunarHour::provider = new DefaultEightCharProvider();

    const vector<string> SolarTerm::NAMES = {
        "冬至", "小寒", "大寒", "立春", "雨水", "惊蛰", "春分", "清明", "谷雨", "立夏", "小满", "芒种", "夏至", "小暑", "大暑", "立秋", "处暑", "白露", "秋分", "寒露", "霜降", "立冬", "小雪", "大雪"
    };

    void SolarTerm::init_by_year(const int year, const int offset) {
        const double jd = floor((year - 2000) * 365.2422 + 180);
        // 355是2000.12冬至，得到较靠近jd的冬至估计值
        double w = floor((jd - 355 + 183) / 365.2422) * 365.2422 + 355;
        if (ShouXingUtil::calc_qi(w) > jd) {
            w -= 365.2422;
        }
        this->year = year;
        cursory_julian_day = ShouXingUtil::calc_qi(w + 15.2184 * offset);
    }

    SolarTerm SolarTerm::from_index(const int year, const int index) {
        return SolarTerm(year, index);
    }

    SolarTerm SolarTerm::from_name(const int year, const string &name) {
        return SolarTerm(year, name);
    }

    SolarTerm SolarTerm::next(const int n) const {
        const int size = get_size();
        const int i = index + n;
        return from_index((year * size + i) / size, index_of(i));
    }

    bool SolarTerm::is_jie() const {
        return index % 2 == 1;
    }

    bool SolarTerm::is_qi() const {
        return index % 2 == 0;
    }

    JulianDay SolarTerm::get_julian_day() const {
        return JulianDay::from_julian_day(ShouXingUtil::qi_accurate2(cursory_julian_day) + JulianDay::J2000);
    }

    SolarDay SolarTerm::get_solar_day() const {
        return JulianDay::from_julian_day(cursory_julian_day + JulianDay::J2000).get_solar_day();
    }

    int SolarTerm::get_year() const {
        return year;
    }

    double SolarTerm::get_cursory_julian_day() const {
        return cursory_julian_day;
    }

    SolarTerm SolarTermDay::get_solar_term() const {
        return solar_term;
    }

    int SolarTermDay::get_day_index() const {
        return day_index;
    }

    string SolarTermDay::get_name() const {
        return solar_term.get_name();
    }

    string SolarTermDay::to_string() const {
        return solar_term.to_string() + "第" + std::to_string(day_index + 1) + "天";
    }

    SolarYear SolarYear::from_year(const int year) {
        return SolarYear(year);
    }

    int SolarYear::get_day_count() const {
        if (1582 == year) {
            return 355;
        }
        if (is_leap()) {
            return 366;
        }
        return 365;
    }

    bool SolarYear::is_leap() const {
        if (year < 1600) {
            return year % 4 == 0;
        }
        return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
    }

    string SolarYear::get_name() const {
        return std::to_string(year) + "年";
    }

    SolarYear SolarYear::next(const int n) const {
        return from_year(year + n);
    }

    int SolarYear::get_year() const {
        return year;
    }

    vector<SolarMonth> SolarYear::get_months() const {
        auto l = vector<SolarMonth>();
        for (int i = 1; i < 13; i++) {
            l.push_back(SolarMonth::from_ym(year, i));
        }
        return l;
    }

    vector<SolarSeason> SolarYear::get_seasons() const {
        auto l = vector<SolarSeason>();
        for (int i = 0; i < 4; i++) {
            l.push_back(SolarSeason::from_index(year, i));
        }
        return l;
    }

    vector<SolarHalfYear> SolarYear::get_half_years() const {
        auto l = vector<SolarHalfYear>();
        for (int i = 0; i < 2; i++) {
            l.push_back(SolarHalfYear::from_index(year, i));
        }
        return l;
    }

    const vector<string> SolarHalfYear::NAMES = {
        "上半年", "下半年"
    };

    SolarHalfYear SolarHalfYear::from_index(const int year, const int index) {
        return SolarHalfYear(year, index);
    }

    SolarYear SolarHalfYear::get_solar_year() const {
        return year;
    }

    int SolarHalfYear::get_year() const {
        return year.get_year();
    }

    int SolarHalfYear::get_index() const {
        return index;
    }

    string SolarHalfYear::get_name() const {
        return NAMES[index];
    }

    string SolarHalfYear::to_string() const {
        return year.to_string() + get_name();
    }

    SolarHalfYear SolarHalfYear::next(const int n) const {
        const int i = index + n;
        return from_index((get_year() * 2 + i) / 2, index_of(i, 2));
    }

    vector<SolarSeason> SolarHalfYear::get_seasons() const {
        auto l = vector<SolarSeason>();
        const int y = get_year();
        for (int i = 0; i < 2; i++) {
            l.push_back(SolarSeason::from_index(y, index * 2 + i));
        }
        return l;
    }

    vector<SolarMonth> SolarHalfYear::get_months() const {
        auto l = vector<SolarMonth>();
        const int y = get_year();
        for (int i = 1; i < 7; i++) {
            l.push_back(SolarMonth::from_ym(y, index * 6 + i));
        }
        return l;
    }

    const vector<string> SolarSeason::NAMES = {
        "一季度", "二季度", "三季度", "四季度"
    };

    SolarSeason SolarSeason::from_index(const int year, const int index) {
        return SolarSeason(year, index);
    }

    SolarYear SolarSeason::get_solar_year() const {
        return year;
    }

    int SolarSeason::get_year() const {
        return year.get_year();
    }

    int SolarSeason::get_index() const {
        return index;
    }

    string SolarSeason::get_name() const {
        return NAMES[index];
    }

    string SolarSeason::to_string() const {
        return year.to_string() + get_name();
    }

    SolarSeason SolarSeason::next(const int n) const {
        const int i = index + n;
        return from_index((get_year() * 4 + i) / 4, index_of(i, 4));
    }

    vector<SolarMonth> SolarSeason::get_months() const {
        auto l = vector<SolarMonth>();
        const int y = get_year();
        for (int i = 1; i < 4; i++) {
            l.push_back(SolarMonth::from_ym(y, index * 3 + i));
        }
        return l;
    }

    const vector<string> SolarMonth::NAMES = {
        "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"
    };

    const int SolarMonth::DAYS[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    SolarMonth SolarMonth::from_ym(const int year, const int month) {
        return SolarMonth(year, month);
    }

    SolarYear SolarMonth::get_solar_year() const {
        return year;
    }

    int SolarMonth::get_year() const {
        return year.get_year();
    }

    int SolarMonth::get_month() const {
        return month;
    }

    int SolarMonth::get_day_count() const {
        if (1582 == get_year() && 10 == month) {
            return 21;
        }
        int d = DAYS[get_index_in_year()];
        //公历闰年2月多一天
        if (2 == month && year.is_leap()) {
            d++;
        }
        return d;
    }

    int SolarMonth::get_index_in_year() const {
        return month - 1;
    }

    SolarSeason SolarMonth::get_season() const {
        return SolarSeason::from_index(get_year(), get_index_in_year() / 3);
    }

    int SolarMonth::get_week_count(const int start) const {
        return static_cast<int>(ceil((index_of(SolarDay::from_ymd(get_year(), month, 1).get_week().get_index() - start, 7) + get_day_count()) / 7.0));
    }

    string SolarMonth::get_name() const {
        return NAMES[get_index_in_year()];
    }

    string SolarMonth::to_string() const {
        return year.to_string() + get_name();
    }

    SolarMonth SolarMonth::next(const int n) const {
        const int i = month - 1 + n;
        return from_ym((get_year() * 12 + i) / 12, index_of(i, 12) + 1);
    }

    vector<SolarWeek> SolarMonth::get_weeks(const int start) const {
        const int size = get_week_count(start);
        const int y = get_year();
        auto l = vector<SolarWeek>();
        for (int i = 0; i < size; i++) {
            l.push_back(SolarWeek::from_ym(y, month, i, start));
        }
        return l;
    }

    vector<SolarDay> SolarMonth::get_days() const {
        const int size = get_day_count();
        const int y = get_year();
        auto l = vector<SolarDay>();
        for (int i = 1; i <= size; i++) {
            l.push_back(SolarDay::from_ymd(y, month, i));
        }
        return l;
    }

    const vector<string> SolarWeek::NAMES = {
        "第一周", "第二周", "第三周", "第四周", "第五周", "第六周"
    };

    SolarWeek SolarWeek::from_ym(const int year, const int month, const int index, const int start) {
        return SolarWeek(year, month, index, start);
    }

    SolarMonth SolarWeek::get_solar_month() const {
        return month;
    }

    int SolarWeek::get_year() const {
        return month.get_year();
    }

    int SolarWeek::get_month() const {
        return month.get_month();
    }

    int SolarWeek::get_index() const {
        return index;
    }

    int SolarWeek::get_index_in_year() const {
        int i = 0;
        const SolarDay first_day = get_first_day();
        // 今年第1周
        SolarWeek w = from_ym(get_year(), 1, 0, start.get_index());
        while (!w.get_first_day().equals(first_day)) {
            w = w.next(1);
            i++;
        }
        return i;
    }

    Week SolarWeek::get_start() const {
        return start;
    }

    string SolarWeek::get_name() const {
        return NAMES[index];
    }

    string SolarWeek::to_string() const {
        return month.to_string() + get_name();
    }

    SolarWeek SolarWeek::next(const int n) const {
        const int start_index = start.get_index();
        int d = index;
        SolarMonth m = month;
        if (n > 0) {
            d += n;
            int week_count = m.get_week_count(start_index);
            while (d >= week_count) {
                d -= week_count;
                m = m.next(1);
                if (!SolarDay::from_ymd(m.get_year(), m.get_month(), 1).get_week().equals(&start)) {
                    d += 1;
                }
                week_count = m.get_week_count(start_index);
            }
        } else if (n < 0) {
            d += n;
            while (d < 0) {
                if (!SolarDay::from_ymd(m.get_year(), m.get_month(), 1).get_week().equals(&start)) {
                    d -= 1;
                }
                m = m.next(-1);
                d += m.get_week_count(start_index);
            }
        }
        return from_ym(m.get_year(), m.get_month(), d, start_index);
    }

    SolarDay SolarWeek::get_first_day() const {
        const SolarDay first_day = SolarDay::from_ymd(get_year(), get_month(), 1);
        return first_day.next(index * 7 - index_of(first_day.get_week().get_index() - start.get_index(), 7));
    }

    vector<SolarDay> SolarWeek::get_days() const {
        auto l = vector<SolarDay>();
        const SolarDay d = get_first_day();
        l.push_back(d);
        for (int i = 1; i < 7; i++) {
            l.push_back(d.next(i));
        }
        return l;
    }

    bool SolarWeek::equals(const SolarWeek &other) const {
        return get_first_day().equals(other.get_first_day());
    }

    const vector<string> SolarDay::NAMES = {
        "1日", "2日", "3日", "4日", "5日", "6日", "7日", "8日", "9日", "10日", "11日", "12日", "13日", "14日", "15日", "16日", "17日", "18日", "19日", "20日", "21日", "22日", "23日", "24日", "25日", "26日", "27日", "28日", "29日", "30日", "31日"
    };

    SolarDay SolarDay::from_ymd(const int year, const int month, const int day) {
        return SolarDay(year, month, day);
    }

    SolarMonth SolarDay::get_solar_month() const {
        return month;
    }

    int SolarDay::get_year() const {
        return month.get_year();
    }

    int SolarDay::get_month() const {
        return month.get_month();
    }

    int SolarDay::get_day() const {
        return day;
    }

    Week SolarDay::get_week() const {
        return get_julian_day().get_week();
    }

    Constellation SolarDay::get_constellation() const {
        const int y = get_month() * 100 + day;
        int i = 8;
        if (y > 1221 || y < 120) {
            i = 9;
        } else if (y < 219) {
            i = 10;
        } else if (y < 321) {
            i = 11;
        } else if (y < 420) {
            i = 0;
        } else if (y < 521) {
            i = 1;
        } else if (y < 622) {
            i = 2;
        } else if (y < 723) {
            i = 3;
        } else if (y < 823) {
            i = 4;
        } else if (y < 923) {
            i = 5;
        } else if (y < 1024) {
            i = 6;
        } else if (y < 1123) {
            i = 7;
        }
        return Constellation::from_index(i);
    }

    string SolarDay::get_name() const {
        return NAMES[day - 1];
    }

    string SolarDay::to_string() const {
        return month.to_string() + get_name();
    }

    JulianDay SolarDay::get_julian_day() const {
        return JulianDay::from_ymd_hms(get_year(), get_month(), day, 0, 0, 0);
    }

    SolarDay SolarDay::next(const int n) const {
        return get_julian_day().next(n).get_solar_day();
    }

    bool SolarDay::is_before(const SolarDay &other) const {
        const int a_year = get_year();
        if (const int b_year = other.get_year(); a_year != b_year) {
            return a_year < b_year;
        }
        const int a_month = get_month();
        if (const int b_month = other.get_month(); a_month != b_month) {
            return a_month < b_month;
        }
        return day < other.get_day();
    }

    bool SolarDay::is_after(const SolarDay &other) const {
        const int a_year = get_year();
        if (const int b_year = other.get_year(); a_year != b_year) {
            return a_year > b_year;
        }
        const int a_month = get_month();
        if (const int b_month = other.get_month(); a_month != b_month) {
            return a_month > b_month;
        }
        return day > other.get_day();
    }

    SolarTerm SolarDay::get_term() const {
        return get_term_day().get_solar_term();
    }

    SolarTermDay SolarDay::get_term_day() const {
        int y = get_year();
        int i = get_month() * 2;
        if (i == 24) {
            y += 1;
            i = 0;
        }
        SolarTerm term = SolarTerm::from_index(y, i);
        SolarDay day = term.get_solar_day();
        while (is_before(day)) {
            term = term.next(-1);
            day = term.get_solar_day();
        }
        return SolarTermDay(term, subtract(day));
    }

    SolarWeek SolarDay::get_solar_week(const int start) const {
        const int y = get_year();
        const int m = get_month();
        return SolarWeek::from_ym(y, m, static_cast<int>(ceil((day + from_ymd(y, m, 1).get_week().next(-start).get_index()) / 7.0)) - 1, start);
    }

    Phenology SolarDay::get_phenology() const {
        return get_phenology_day().get_phenology();
    }

    PhenologyDay SolarDay::get_phenology_day() const {
        const SolarTermDay d = get_term_day();
        const int day_index = d.get_day_index();
        int index = day_index / 5;
        if (index > 2) {
            index = 2;
        }
        const SolarTerm term = d.get_solar_term();
        Phenology p = Phenology::from_index(term.get_year(), term.get_index() * 3 + index);
        return PhenologyDay(p, day_index - index * 5);
    }

    optional<DogDay> SolarDay::get_dog_day() const {
        // 夏至
        const SolarTerm xia_zhi = SolarTerm::from_index(get_year(), 12);
        SolarDay start = xia_zhi.get_solar_day();
        // 第3个庚日，即初伏第1天
        start = start.next(start.get_lunar_day().get_sixty_cycle().get_heaven_stem().steps_to(6) + 20);
        int days = subtract(start);
        // 初伏以前
        if (days < 0) {
            return nullopt;
        }
        if (days < 10) {
            Dog d = Dog::from_index(0);
            return DogDay(d, days);
        }
        // 第4个庚日，中伏第1天
        start = start.next(10);
        days = subtract(start);
        if (days < 10) {
            Dog d = Dog::from_index(1);
            return DogDay(d, days);
        }
        // 第5个庚日，中伏第11天或末伏第1天
        start = start.next(10);
        days = subtract(start);
        // 立秋
        if (xia_zhi.next(3).get_solar_day().is_after(start)) {
            if (days < 10) {
                Dog d = Dog::from_index(1);
                return DogDay(d, days + 10);
            }
            start = start.next(10);
            days = subtract(start);
        }
        if (days >= 10) {
            return nullopt;
        }
        Dog d = Dog::from_index(2);
        return DogDay(d, days);
    }

    optional<NineDay> SolarDay::get_nine_day() const {
        const int year = get_year();
        SolarDay start = SolarTerm::from_index(year + 1, 0).get_solar_day();
        if (is_before(start)) {
            start = SolarTerm::from_index(year, 0).get_solar_day();
        }
        if (const SolarDay end = start.next(81); is_before(start) || !is_before(end)) {
            return nullopt;
        }
        const int days = subtract(start);
        Nine n = Nine::from_index(days / 9);
        return NineDay(n, days % 9);
    }

    optional<PlumRainDay> SolarDay::get_plum_rain_day() const {
        // 芒种
        const SolarTerm grain_in_ear = SolarTerm::from_index(get_year(), 11);
        SolarDay start = grain_in_ear.get_solar_day();
        // 芒种后的第1个丙日
        start = start.next(start.get_lunar_day().get_sixty_cycle().get_heaven_stem().steps_to(2));

        // 小暑
        SolarDay end = grain_in_ear.next(2).get_solar_day();
        // 小暑后的第1个未日
        end = end.next(end.get_lunar_day().get_sixty_cycle().get_earth_branch().steps_to(7));

        if (is_before(start) || is_after(end)) {
            return nullopt;
        }
        if (equals(end)) {
            PlumRain p = PlumRain::from_index(1);
            return PlumRainDay(p, 0);
        }
        PlumRain p = PlumRain::from_index(0);
        return PlumRainDay(p, subtract(start));
    }

    HideHeavenStemDay SolarDay::get_hide_heaven_stem_day() const {
        const int day_counts[] = {3, 5, 7, 9, 10, 30};
        SolarTerm term = get_term();
        if (term.is_qi()) {
            term = term.next(-1);
        }
        int day_index = subtract(term.get_solar_day());
        const int start_index = (term.get_index() - 1) * 3;
        string data = "93705542220504xx1513904541632524533533105544806564xx7573304542018584xx95";
        data = data.substr(start_index, 6);
        int days = 0;
        int heaven_stem_index = 0;
        int type_index = 0;
        while (type_index < 3) {
            const int i = type_index * 2;
            string d = data.substr(i, 1);
            int count = 0;
            if (d != "x") {
                heaven_stem_index = stoi(d);
                count = day_counts[stoi(data.substr(i + 1, 1))];
                days += count;
            }
            if (day_index <= days) {
                day_index -= days - count;
                break;
            }
            type_index++;
        }
        HideHeavenStemType type;
        switch (type_index) {
            case 1:
                type = HideHeavenStemType::MIDDLE;
                break;
            case 2:
                type = HideHeavenStemType::MAIN;
                break;
            default:
                type = HideHeavenStemType::RESIDUAL;
        }
        return HideHeavenStemDay(HideHeavenStem(heaven_stem_index, type), day_index);
    }

    int SolarDay::get_index_in_year() const {
        return subtract(from_ymd(get_year(), 1, 1));
    }

    int SolarDay::subtract(const SolarDay &other) const {
        return static_cast<int>(get_julian_day().subtract(other.get_julian_day()));
    }

    bool SolarDay::equals(const SolarDay &other) const {
        return to_string() == other.to_string();
    }

    LunarDay SolarDay::get_lunar_day() const {
        LunarMonth m = LunarMonth::from_ym(get_year(), get_month());
        int days = subtract(m.get_first_julian_day().get_solar_day());
        while (days < 0) {
            m = m.next(-1);
            days += m.get_day_count();
        }
        return LunarDay::from_ymd(m.get_year(), m.get_month_with_leap(), days + 1);
    }

    SixtyCycleDay SolarDay::get_sixty_cycle_day() const {
        return SixtyCycleDay::from_solar_day(*this);
    }

    RabByungDay SolarDay::get_rab_byung_day() const {
        return RabByungDay::from_solar_day(*this);
    }

    optional<SolarFestival> SolarDay::get_festival() const {
        return SolarFestival::from_ymd(get_year(), get_month(), get_day());
    }

    PhaseDay SolarDay::get_phase_day() const {
        const LunarMonth month = get_lunar_day().get_lunar_month().next(1);
        Phase p = Phase::from_index(month.get_year(), month.get_month_with_leap(), 0);
        SolarDay d = p.get_solar_day();
        while (d.is_after(*this)) {
            p = p.next(-1);
            d = p.get_solar_day();
        }
        return PhaseDay(p, subtract(d));
    }

    Phase SolarDay::get_phase() const {
        return get_phase_day().get_phase();
    }

    SolarTime &SolarTime::operator=(const SolarTime &other) {
        day = other.day;
        hour = other.hour;
        minute = other.minute;
        second = other.second;
        return *this;
    }

    SolarTime SolarTime::from_ymd_hms(const int year, const int month, const int day, const int hour, const int minute, const int second) {
        return SolarTime(year, month, day, hour, minute, second);
    }

    SolarDay SolarTime::get_solar_day() const {
        return day;
    }

    int SolarTime::get_year() const {
        return day.get_year();
    }

    int SolarTime::get_month() const {
        return day.get_month();
    }

    int SolarTime::get_day() const {
        return day.get_day();
    }

    int SolarTime::get_hour() const {
        return hour;
    }

    int SolarTime::get_minute() const {
        return minute;
    }

    int SolarTime::get_second() const {
        return second;
    }

    string SolarTime::get_name() const {
        char buffer[9];
        snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d", hour, minute, second);
        return {buffer};
    }

    string SolarTime::to_string() const {
        return day.to_string() + " " + get_name();
    }

    SolarTime SolarTime::next(const int n) const {
        if (n == 0) {
            return from_ymd_hms(get_year(), get_month(), get_day(), hour, minute, second);
        }
        int ts = second + n;
        int tm = minute + ts / 60;
        ts %= 60;
        if (ts < 0) {
            ts += 60;
            tm -= 1;
        }
        int th = hour + tm / 60;
        tm %= 60;
        if (tm < 0) {
            tm += 60;
            th -= 1;
        }
        int td = th / 24;
        th %= 24;
        if (th < 0) {
            th += 24;
            td -= 1;
        }

        const SolarDay d = day.next(td);
        return from_ymd_hms(d.get_year(), d.get_month(), d.get_day(), th, tm, ts);
    }

    bool SolarTime::is_before(const SolarTime &other) const {
        if (!day.equals(other.get_solar_day())) {
            return day.is_before(other.get_solar_day());
        }
        if (hour != other.get_hour()) {
            return hour < other.get_hour();
        }
        if (minute != other.get_minute()) {
            return minute < other.get_minute();
        }
        return second < other.get_second();
    }

    bool SolarTime::is_after(const SolarTime &other) const {
        if (!day.equals(other.get_solar_day())) {
            return day.is_after(other.get_solar_day());
        }
        if (hour != other.get_hour()) {
            return hour > other.get_hour();
        }
        if (minute != other.get_minute()) {
            return minute > other.get_minute();
        }
        return second > other.get_second();
    }

    SolarTerm SolarTime::get_term() const {
        SolarTerm term = day.get_term();
        if (is_before(term.get_julian_day().get_solar_time())) {
            term = term.next(-1);
        }
        return term;
    }

    Phenology SolarTime::get_phenology() const {
        Phenology p = day.get_phenology();
        if (is_before(p.get_julian_day().get_solar_time())) {
            p = p.next(-1);
        }
        return p;
    }

    JulianDay SolarTime::get_julian_day() const {
        return JulianDay::from_ymd_hms(get_year(), get_month(), get_day(), hour, minute, second);
    }

    int SolarTime::subtract(const SolarTime &other) const {
        int days = day.subtract(other.get_solar_day());
        const int cs = hour * 3600 + minute * 60 + second;
        const int ts = other.get_hour() * 3600 + other.get_minute() * 60 + other.get_second();
        int seconds = cs - ts;
        if (seconds < 0) {
            seconds += 86400;
            days--;
        }
        seconds += days * 86400;
        return seconds;
    }

    bool SolarTime::equals(const SolarTime &other) const {
        return to_string() == other.to_string();
    }

    SixtyCycleHour SolarTime::get_sixty_cycle_hour() const {
        return SixtyCycleHour::from_solar_time(*this);
    }

    LunarHour SolarTime::get_lunar_hour() const {
        const LunarDay d = day.get_lunar_day();
        return LunarHour::from_ymd_hms(d.get_year(), d.get_month(), d.get_day(), hour, minute, second);
    }

    Phase SolarTime::get_phase() const {
        const LunarMonth month = get_lunar_hour().get_lunar_day().get_lunar_month().next(1);
        Phase p = Phase::from_index(month.get_year(), month.get_month_with_leap(), 0);
        while (p.get_solar_time().is_after(*this)) {
            p = p.next(-1);
        }
        return p;
    }

    SixtyCycle ThreePillars::get_year() const {
        return year;
    }

    SixtyCycle ThreePillars::get_month() const {
        return month;
    }

    SixtyCycle ThreePillars::get_day() const {
        return day;
    }

    vector<SolarDay> ThreePillars::get_solar_days(const int start_year, const int end_year) const {
        auto l = vector<SolarDay>();
        // 月地支距寅月的偏移值
        int m = month.get_earth_branch().next(-2).get_index();
        // 月天干要一致
        if (!HeavenStem::from_index((year.get_heaven_stem().get_index() + 1) * 2 + m).equals(month.get_heaven_stem())) {
            return l;
        }
        // 1年的立春是辛酉，序号57
        int y = year.next(-57).get_index() + 1;
        // 节令偏移值
        m *= 2;
        if (const int base_year = start_year - 1; base_year > y) {
            y += 60 * static_cast<int>(ceil((base_year - y) / 60.0));
        }
        while (y <= end_year) {
            // 立春为寅月的开始
            SolarTerm term = SolarTerm::from_index(y, 3);
            // 节令推移，年干支和月干支就都匹配上了
            if (m > 0) {
                term = term.next(m);
            }
            if (SolarDay solar_day = term.get_solar_day(); solar_day.get_year() >= start_year) {
                // 日干支和节令干支的偏移值
                int d = day.next(-solar_day.get_lunar_day().get_sixty_cycle().get_index()).get_index();
                if (d > 0) {
                    // 从节令推移天数
                    solar_day = solar_day.next(d);
                }
                // 验证一下
                if (solar_day.get_sixty_cycle_day().get_three_pillars().equals(this)) {
                    l.push_back(solar_day);
                }
            }
            y += 60;
        }
        return l;
    }

    string ThreePillars::get_name() const {
        return year.to_string() + " " + month.to_string() + " " + day.to_string();
    }

    SixtyCycle EightChar::get_year() const {
        return three_pillars.get_year();
    }

    SixtyCycle EightChar::get_month() const {
        return three_pillars.get_month();
    }

    SixtyCycle EightChar::get_day() const {
        return three_pillars.get_day();
    }

    SixtyCycle EightChar::get_hour() const {
        return hour;
    }

    SixtyCycle EightChar::get_fetal_origin() const {
        const SixtyCycle m = get_month();
        return SixtyCycle::from_name(m.get_heaven_stem().next(1).get_name() + m.get_earth_branch().next(3).get_name());
    }

    SixtyCycle EightChar::get_fetal_breath() const {
        const SixtyCycle d = get_day();
        return SixtyCycle::from_name(d.get_heaven_stem().next(5).get_name() + EarthBranch::from_index(13 - d.get_earth_branch().get_index()).get_name());
    }

    SixtyCycle EightChar::get_own_sign() const {
        int m = get_month().get_earth_branch().get_index() - 1;
        if (m < 1) {
            m += 12;
        }
        int h = hour.get_earth_branch().get_index() - 1;
        if (h < 1) {
            h += 12;
        }
        int offset = m + h;
        offset = (offset >= 14 ? 26 : 14) - offset;
        return SixtyCycle::from_name(HeavenStem::from_index((get_year().get_heaven_stem().get_index() + 1) * 2 + offset - 1).get_name() + EarthBranch::from_index(offset + 1).get_name());
    }

    SixtyCycle EightChar::get_body_sign() const {
        int offset = get_month().get_earth_branch().get_index() - 1;
        if (offset < 1) {
            offset += 12;
        }
        offset += hour.get_earth_branch().get_index() + 1;
        if (offset > 12) {
            offset -= 12;
        }
        return SixtyCycle::from_name(HeavenStem::from_index((get_year().get_heaven_stem().get_index() + 1) * 2 + offset - 1).get_name() + EarthBranch::from_index(offset + 1).get_name());
    }

    vector<SolarTime> EightChar::get_solar_times(const int start_year, const int end_year) const {
        const SixtyCycle year = get_year();
        const SixtyCycle month = get_year();
        const SixtyCycle day = get_year();
        auto l = vector<SolarTime>();
        // 月地支距寅月的偏移值
        int m = month.get_earth_branch().next(-2).get_index();
        // 月天干要一致
        if (!HeavenStem::from_index((year.get_heaven_stem().get_index() + 1) * 2 + m).equals(month.get_heaven_stem())) {
            return l;
        }
        // 1年的立春是辛酉，序号57
        int y = year.next(-57).get_index() + 1;
        // 节令偏移值
        m *= 2;
        // 时辰地支转时刻
        const int h = hour.get_earth_branch().get_index() * 2;
        // 兼容子时多流派
        auto hours = vector<int>();
        hours.push_back(h);
        if (h == 0) {
            hours.push_back(23);
        }
        if (const int base_year = start_year - 1; base_year > y) {
            y += 60 * static_cast<int>(ceil((base_year - y) / 60.0));
        }
        while (y <= end_year) {
            // 立春为寅月的开始
            SolarTerm term = SolarTerm::from_index(y, 3);
            // 节令推移，年干支和月干支就都匹配上了
            if (m > 0) {
                term = term.next(m);
            }
            if (SolarTime solar_time = term.get_julian_day().get_solar_time(); solar_time.get_year() >= start_year) {
                // 日干支和节令干支的偏移值
                SolarDay solar_day = solar_time.get_solar_day();
                int d = day.next(-solar_day.get_lunar_day().get_sixty_cycle().get_index()).get_index();
                if (d > 0) {
                    // 从节令推移天数
                    solar_day = solar_day.next(d);
                }
                for (const int hour: hours) {
                    int mi = 0;
                    int s = 0;
                    // 如果正好是节令当天，且小时和节令的小时数相等的极端情况，把分钟和秒钟带上
                    if (d == 0 && hour == solar_time.get_hour()) {
                        mi = solar_time.get_minute();
                        s = solar_time.get_second();
                    }
                    SolarTime time = SolarTime::from_ymd_hms(solar_day.get_year(), solar_day.get_month(), solar_day.get_day(), hour, mi, s);
                    if (d == 30) {
                        time = time.next(-3600);
                    }
                    // 验证一下
                    if (time.get_lunar_hour().get_eight_char().equals(this)) {
                        l.push_back(time);
                    }
                }
            }
            y += 60;
        }
        return l;
    }

    string EightChar::get_name() const {
        return three_pillars.to_string() + " " + hour.to_string();
    }

    EightChar DefaultEightCharProvider::get_eight_char(const LunarHour hour) const {
        return hour.get_sixty_cycle_hour().get_eight_char();
    }

    EightChar LunarSect2EightCharProvider::get_eight_char(const LunarHour hour) const {
        const SixtyCycleHour h = hour.get_sixty_cycle_hour();
        return EightChar(h.get_year(), h.get_month(), hour.get_lunar_day().get_sixty_cycle(), h.get_sixty_cycle());
    }

    SolarTime ChildLimitInfo::get_start_time() const {
        return start_time;
    }

    SolarTime ChildLimitInfo::get_end_time() const {
        return end_time;
    }

    int ChildLimitInfo::get_year_count() const {
        return year_count;
    }

    int ChildLimitInfo::get_month_count() const {
        return month_count;
    }

    int ChildLimitInfo::get_day_count() const {
        return day_count;
    }

    int ChildLimitInfo::get_hour_count() const {
        return hour_count;
    }

    int ChildLimitInfo::get_minute_count() const {
        return minute_count;
    }

    bool ChildLimit::get_forward(const EightChar &eight_char, const Gender gender) {
        // 阳男阴女顺推，阴男阳女逆推
        const bool yang = YinYang::YANG == eight_char.get_year().get_heaven_stem().get_yin_yang();
        const bool man = Gender::MAN == gender;
        return (yang && man) || (!yang && !man);
    }

    ChildLimitProvider* ChildLimit::provider = new DefaultChildLimitProvider();

    ChildLimitInfo ChildLimit::get_info(const SolarTime &birth_time, const bool forward) {
        SolarTerm term = birth_time.get_term();
        if (!term.is_jie()) {
            term = term.next(-1);
        }
        if (forward) {
            term = term.next(2);
        }
        return provider->get_info(birth_time, term);
    }

    Fortune Fortune::from_child_limit(const ChildLimit& child_limit, const int index) {
        return Fortune(child_limit, index);
    }

    int Fortune::get_age() const {
        return child_limit.get_end_sixty_cycle_year().get_year() - child_limit.get_start_sixty_cycle_year().get_year() + 1 + index;
    }

    SixtyCycleYear Fortune::get_sixty_cycle_year() const {
        return child_limit.get_end_sixty_cycle_year().next(index);
    }

    SixtyCycle Fortune::get_sixty_cycle() const {
        const int n = get_age();
        return child_limit.get_eight_char().get_hour().next(child_limit.is_forward() ? n: -n);
    }

    string Fortune::get_name() const {
        return get_sixty_cycle().get_name();
    }

    Fortune Fortune::next(const int n) const {
        return from_child_limit(child_limit, index + n);
    }

    DecadeFortune DecadeFortune::from_child_limit(const ChildLimit& child_limit, const int index) {
        return DecadeFortune(child_limit, index);
    }

    int DecadeFortune::get_start_age() const {
        return child_limit.get_end_sixty_cycle_year().get_year() - child_limit.get_start_sixty_cycle_year().get_year() + 1 + index * 10;
    }

    int DecadeFortune::get_end_age() const {
        return get_start_age() + 9;
    }

    SixtyCycle DecadeFortune::get_sixty_cycle() const {
        return child_limit.get_eight_char().get_month().next(child_limit.is_forward() ? index + 1 : -index - 1);
    }

    SixtyCycleYear DecadeFortune::get_start_sixty_cycle_year() const {
        return child_limit.get_end_sixty_cycle_year().next(index * 10);
    }

    SixtyCycleYear DecadeFortune::get_end_sixty_cycle_year() const {
        return get_start_sixty_cycle_year().next(9);
    }

    string DecadeFortune::get_name() const {
        return get_sixty_cycle().get_name();
    }

    DecadeFortune DecadeFortune::next(const int n) const {
        return from_child_limit(child_limit, index + n);
    }

    Fortune DecadeFortune::get_start_fortune() const {
        return Fortune::from_child_limit(child_limit, index * 10);
    }

    ChildLimit ChildLimit::from_solar_time(const SolarTime& birth_time, const Gender gender) {
        return ChildLimit(birth_time, gender);
    }

    EightChar ChildLimit::get_eight_char() const {
        return eight_char;
    }

    Gender ChildLimit::get_gender() const {
        return gender;
    }

    bool ChildLimit::is_forward() const {
        return forward;
    }

    int ChildLimit::get_year_count() const {
        return info.get_year_count();
    }

    int ChildLimit::get_month_count() const {
        return info.get_month_count();
    }

    int ChildLimit::get_day_count() const {
        return info.get_day_count();
    }

    int ChildLimit::get_hour_count() const {
        return info.get_hour_count();
    }

    int ChildLimit::get_minute_count() const {
        return info.get_minute_count();
    }

    SolarTime ChildLimit::get_start_time() const {
        return info.get_start_time();
    }

    SolarTime ChildLimit::get_end_time() const {
        return info.get_end_time();
    }

    DecadeFortune ChildLimit::get_start_decade_fortune() const {
        return DecadeFortune::from_child_limit(*this, 0);
    }

    DecadeFortune ChildLimit::get_decade_fortune() const {
        return DecadeFortune::from_child_limit(*this, -1);
    }

    Fortune ChildLimit::get_start_fortune() const {
        return Fortune::from_child_limit(*this, 0);
    }

    SixtyCycleYear ChildLimit::get_start_sixty_cycle_year() const {
        return SixtyCycleYear::from_year(get_start_time().get_year());
    }

    SixtyCycleYear ChildLimit::get_end_sixty_cycle_year() const {
        return SixtyCycleYear::from_year(get_end_time().get_year());
    }

    int ChildLimit::get_start_age() const {
        return 1;
    }

    int ChildLimit::get_end_age() const {
        const int n = get_end_sixty_cycle_year().get_year() - get_start_sixty_cycle_year().get_year();
        if (n > 1) {
            return n;
        }
        return 1;
    }

    ChildLimitInfo AbstractChildLimitProvider::next(const SolarTime& birth_time, const int add_year, const int add_month, const int add_day, const int add_hour, const int add_minute, const int add_second) const {
        int d = birth_time.get_day() + add_day;
        int h = birth_time.get_hour() + add_hour;
        int mi = birth_time.get_minute() + add_minute;
        int s = birth_time.get_second() + add_second;
        mi += s / 60;
        s %= 60;
        h += mi / 60;
        mi %= 60;
        d += h / 24;
        h %= 24;

        SolarMonth sm = SolarMonth::from_ym(birth_time.get_year() + add_year, birth_time.get_month()).next(add_month);

        int dc = sm.get_day_count();
        while (d > dc) {
            d -= dc;
            sm = sm.next(1);
            dc = sm.get_day_count();
        }

        return ChildLimitInfo(birth_time, SolarTime::from_ymd_hms(sm.get_year(), sm.get_month(), d, h, mi, s), add_year, add_month, add_day, add_hour, add_minute);
    }

    ChildLimitInfo DefaultChildLimitProvider::get_info(const SolarTime birth_time, const SolarTerm term) const {
        // 出生时刻和节令时刻相差的秒数
        int seconds = abs(term.get_julian_day().get_solar_time().subtract(birth_time));
        // 3天 = 1年，3天=60*60*24*3秒=259200秒 = 1年
        const int year = seconds / 259200;
        seconds %= 259200;
        // 1天 = 4月，1天=60*60*24秒=86400秒 = 4月，85400秒/4=21600秒 = 1月
        const int month = seconds / 21600;
        seconds %= 21600;
        // 1时 = 5天，1时=60*60秒=3600秒 = 5天，3600秒/5=720秒 = 1天
        const int day = seconds / 720;
        seconds %= 720;
        // 1分 = 2时，60秒 = 2时，60秒/2=30秒 = 1时
        const int hour = seconds / 30;
        seconds %= 30;
        // 1秒 = 2分，1秒/2=0.5秒 = 1分
        const int minute = seconds * 2;

        return next(birth_time, year, month, day, hour, minute, 0);
    }

    ChildLimitInfo China95ChildLimitProvider::get_info(const SolarTime birth_time, const SolarTerm term) const {
        // 出生时刻和节令时刻相差的分钟数
        int minutes = abs(term.get_julian_day().get_solar_time().subtract(birth_time)) / 60;
        const int year = minutes / 4320;
        minutes %= 4320;
        const int month = minutes / 360;
        minutes %= 360;
        const int day = minutes / 12;

        return next(birth_time, year, month, day, 0, 0, 0);
    }

    ChildLimitInfo LunarSect1ChildLimitProvider::get_info(const SolarTime birth_time, const SolarTerm term) const {
        const SolarTime term_time = term.get_julian_day().get_solar_time();
        SolarTime end = term_time;
        SolarTime start = birth_time;
        if (birth_time.is_after(term_time)) {
            end = birth_time;
            start = term_time;
        }
        const int end_time_zhi_index = end.get_hour() == 23 ? 11 : end.get_lunar_hour().get_index_in_day();
        const int start_time_zhi_index = start.get_hour() == 23 ? 11 : start.get_lunar_hour().get_index_in_day();
        // 时辰差
        int hour_diff = end_time_zhi_index - start_time_zhi_index;
        // 天数差
        int day_diff = end.get_solar_day().subtract(start.get_solar_day());
        if (hour_diff < 0) {
            hour_diff += 12;
            day_diff--;
        }
        const int month_diff = hour_diff * 10 / 30;
        int month = day_diff * 4 + month_diff;
        const int day = hour_diff * 10 - month_diff * 30;
        const int year = month / 12;
        month = month - year * 12;

        return next(birth_time, year, month, day, 0, 0, 0);
    }

    ChildLimitInfo LunarSect2ChildLimitProvider::get_info(const SolarTime birth_time, const SolarTerm term) const {
        // 出生时刻和节令时刻相差的分钟数
        int minutes = abs(term.get_julian_day().get_solar_time().subtract(birth_time)) / 60;
        const int year = minutes / 4320;
        minutes %= 4320;
        const int month = minutes / 360;
        minutes %= 360;
        const int day = minutes / 12;
        minutes %= 12;
        const int hour = minutes * 2;

        return next(birth_time, year, month, day, hour, 0, 0);
    }

    const vector<string> LegalHoliday::NAMES = {
        "元旦节", "春节", "清明节", "劳动节", "端午节", "中秋节", "国庆节", "国庆中秋", "抗战胜利日"
    };

    string LegalHoliday::DATA = "2001122900+032001123000+022002010110+002002010210-012002010310-022002020901+032002021001+022002021211+002002021311-012002021411-022002021511-032002021611-042002021711-052002021811-062002042703+042002042803+032002050113+002002050213-012002050313-022002050413-032002050513-042002050613-052002050713-062002092806+032002092906+022002100116+002002100216-012002100316-022002100416-032002100516-042002100616-052002100716-062003010110+002003020111+002003020211-012003020311-022003020411-032003020511-042003020611-052003020711-062003020801-072003020901-082003042603+052003042703+042003050113+002003050213-012003050313-022003050413-032003050513-042003050613-052003050713-062003092706+042003092806+032003100116+002003100216-012003100316-022003100416-032003100516-042003100616-052003100716-062004010110+002004011701+052004011801+042004012211+002004012311-012004012411-022004012511-032004012611-042004012711-052004012811-062004050113+002004050213-012004050313-022004050413-032004050513-042004050613-052004050713-062004050803-072004050903-082004100116+002004100216-012004100316-022004100416-032004100516-042004100616-052004100716-062004100906-082004101006-092005010110+002005010210-012005010310-022005020501+042005020601+032005020911+002005021011-012005021111-022005021211-032005021311-042005021411-052005021511-062005043003+012005050113+002005050213-012005050313-022005050413-032005050513-042005050613-052005050713-062005050803-072005100116+002005100216-012005100316-022005100416-032005100516-042005100616-052005100716-062005100806-072005100906-082005123100+012006010110+002006010210-012006010310-022006012801+012006012911+002006013011-012006013111-022006020111-032006020211-042006020311-052006020411-062006020501-072006042903+022006043003+012006050113+002006050213-012006050313-022006050413-032006050513-042006050613-052006050713-062006093006+012006100116+002006100216-012006100316-022006100416-032006100516-042006100616-052006100716-062006100806-072006123000+022006123100+012007010110+002007010210-012007010310-022007021701+012007021811+002007021911-012007022011-022007022111-032007022211-042007022311-052007022411-062007022501-072007042803+032007042903+022007050113+002007050213-012007050313-022007050413-032007050513-042007050613-052007050713-062007092906+022007093006+012007100116+002007100216-012007100316-022007100416-032007100516-042007100616-052007100716-062007122900+032007123010+022007123110+012008010110+002008020201+042008020301+032008020611+002008020711-012008020811-022008020911-032008021011-042008021111-052008021211-062008040412+002008040512-012008040612-022008050113+002008050213-012008050313-022008050403-032008060714+012008060814+002008060914-012008091315+012008091415+002008091515-012008092706+042008092806+032008092916+022008093016+012008100116+002008100216-012008100316-022008100416-032008100516-042009010110+002009010210-012009010310-022009010400-032009012401+012009012511+002009012611-012009012711-022009012811-032009012911-042009013011-052009013111-062009020101-072009040412+002009040512-012009040612-022009050113+002009050213-012009050313-022009052814+002009052914-012009053014-022009053104-032009092706+042009100116+002009100216-012009100316-022009100416-032009100515-022009100615-032009100715-042009100815-052009101005-072010010110+002010010210-012010010310-022010021311+002010021411-012010021511-022010021611-032010021711-042010021811-052010021911-062010022001-072010022101-082010040312+022010040412+012010040512+002010050113+002010050213-012010050313-022010061204+042010061304+032010061414+022010061514+012010061614+002010091905+032010092215+002010092315-012010092415-022010092505-032010092606+052010100116+002010100216-012010100316-022010100416-032010100516-042010100616-052010100716-062010100906-082011010110+002011010210-012011010310-022011013001+042011020211+012011020311+002011020411-012011020511-022011020611-032011020711-042011020811-052011021201-092011040202+032011040312+022011040412+012011040512+002011043013+012011050113+002011050213-012011060414+022011060514+012011060614+002011091015+022011091115+012011091215+002011100116+002011100216-012011100316-022011100416-032011100516-042011100616-052011100716-062011100806-072011100906-082011123100+012012010110+002012010210-012012010310-022012012101+022012012211+012012012311+002012012411-012012012511-022012012611-032012012711-042012012811-052012012901-062012033102+042012040102+032012040212+022012040312+012012040412+002012042803+032012042913+022012043013+012012050113+002012050203-012012062214+012012062314+002012062414-012012092905+012012093015+002012100116+002012100216-012012100316-022012100416-032012100516-042012100616-052012100716-062012100806-072013010110+002013010210-012013010310-022013010500-042013010600-052013020911+012013021011+002013021111-012013021211-022013021311-032013021411-042013021511-052013021601-062013021701-072013040412+002013040512-012013040612-022013042703+042013042803+032013042913+022013043013+012013050113+002013060804+042013060904+032013061014+022013061114+012013061214+002013091915+002013092015-012013092115-022013092205-032013092906+022013100116+002013100216-012013100316-022013100416-032013100516-042013100616-052013100716-062014010110+002014012601+052014013111+002014020111-012014020211-022014020311-032014020411-042014020511-052014020611-062014020801-082014040512+002014040612-012014040712-022014050113+002014050213-012014050313-022014050403-032014053114+022014060114+012014060214+002014090615+022014090715+012014090815+002014092806+032014100116+002014100216-012014100316-022014100416+002014100516-042014100616-052014100716-062014101106-102015010110+002015010210-012015010310-022015010400-032015021501+042015021811+012015021911+002015022011-012015022111-022015022211-032015022311-042015022411-052015022801-092015040412+012015040512+002015040612-012015050113+002015050213-012015050313-022015062014+002015062114-012015062214-022015090318+002015090418-012015090518-022015090608-032015092615+012015092715+002015100116+002015100216-012015100316-022015100416+002015100516-042015100616-052015100716-062015101006-092016010110+002016010210-012016010310-022016020601+022016020711+012016020811+002016020911-012016021011-022016021111-032016021211-042016021311-052016021401-062016040212+022016040312+012016040412+002016043013+012016050113+002016050213-012016060914+002016061014-012016061114-022016061204-032016091515+002016091615-012016091715-022016091805-032016100116+002016100216-012016100316-022016100416-032016100516-042016100616-052016100716-062016100806-072016100906-082016123110+012017010110+002017010210-012017012201+062017012711+012017012811+002017012911-012017013011-022017013111-032017020111-042017020211-052017020401-072017040102+032017040212+022017040312+012017040412+002017042913+022017043013+012017050113+002017052704+032017052814+022017052914+012017053014+002017093006+012017100116+002017100216-012017100316-022017100415+002017100516-042017100616-052017100716-062017100816-072017123010+022017123110+012018010110+002018021101+052018021511+012018021611+002018021711-012018021811-022018021911-032018022011-042018022111-052018022401-082018040512+002018040612-012018040712-022018040802-032018042803+032018042913+022018043013+012018050113+002018061614+022018061714+012018061814+002018092215+022018092315+012018092415+002018092906+022018093006+012018100116+002018100216-012018100316-022018100416-032018100516-042018100616-052018100716-062018122900+032018123010+022018123110+012019010110+002019020201+032019020301+022019020411+012019020511+002019020611-012019020711-022019020811-032019020911-042019021011-052019040512+002019040612-012019040712-022019042803+032019050113+002019050213-012019050313-022019050413-032019050503-042019060714+002019060814-012019060914-022019091315+002019091415-012019091515-022019092906+022019100116+002019100216-012019100316-022019100416-032019100516-042019100616-052019100716-062019101206-112020010110+002020011901+062020012411+012020012511+002020012611-012020012711-022020012811-032020012911-042020013011-052020013111-062020020111-072020020211-082020040412+002020040512-012020040612-022020042603+052020050113+002020050213-012020050313-022020050413-032020050513-042020050903-082020062514+002020062614-012020062714-022020062804-032020092707+042020100117+002020100216-012020100316-022020100416-032020100516-042020100616-052020100716-062020100816-072020101006-092021010110+002021010210-012021010310-022021020701+052021021111+012021021211+002021021311-012021021411-022021021511-032021021611-042021021711-052021022001-082021040312+012021040412+002021040512-012021042503+062021050113+002021050213-012021050313-022021050413-032021050513-042021050803-072021061214+022021061314+012021061414+002021091805+032021091915+022021092015+012021092115+002021092606+052021100116+002021100216-012021100316-022021100416-032021100516-042021100616-052021100716-062021100906-082022010110+002022010210-012022010310-022022012901+032022013001+022022013111+012022020111+002022020211-012022020311-022022020411-032022020511-042022020611-052022040202+032022040312+022022040412+012022040512+002022042403+072022043013+012022050113+002022050213-012022050313-022022050413-032022050703-062022060314+002022060414-012022060514-022022091015+002022091115-012022091215-022022100116+002022100216-012022100316-022022100416-032022100516-042022100616-052022100716-062022100806-072022100906-082022123110+012023010110+002023010210-012023012111+012023012211+002023012311-012023012411-022023012511-032023012611-042023012711-052023012801-062023012901-072023040512+002023042303+082023042913+022023043013+012023050113+002023050213-012023050313-022023050603-052023062214+002023062314-012023062414-022023062504-032023092915+002023093016+012023100116+002023100216-012023100316-022023100416-032023100516-042023100616-052023100706-062023100806-072023123010+022023123110+012024010110+002024020401+062024021011+002024021111-012024021211-022024021311-032024021411-042024021511-052024021611-062024021711-072024021801-082024040412+002024040512-012024040612-022024040702-032024042803+032024050113+002024050213-012024050313-022024050413-032024050513-042024051103-102024060814+022024060914+012024061014+002024091405+032024091515+022024091615+012024091715+002024092906+022024100116+002024100216-012024100316-022024100416-032024100516-042024100616-052024100716-062024101206-112025010110+002025012601+032025012811+012025012911+002025013011-012025013111-022025020111-032025020211-032025020311-042025020411-052025020801-092025040412+002025040512-012025040612-022025042703+042025050113+002025050213-012025050313-022025050413-032025050513-042025053114+002025060114-012025060214-022025092807+032025100117+002025100217-012025100317-022025100417-032025100517-042025100617-052025100717-062025100817-072025101107-102026010110+002026010210-012026010310-022026010400-032026021401+032026021511+022026021611+012026021711+002026021811-012026021911-022026022011-032026022111-042026022211-052026022311-062026022801-112026040412+012026040512+002026040612-012026050113+002026050213-012026050313-022026050413-032026050513-042026050903-082026061914+002026062014-012026062114-022026092006+052026092515+002026092615-012026092715-022026100116+002026100216-012026100316-022026100416-032026100516-042026100616-052026100716-062026101006-09";

    SolarDay LegalHoliday::compute_target(const string &data) const {
        const int offset = stoi(data.substr(data.length()-2));
        return day.next('-' == data[10] ? -offset : offset);
    }

    optional<LegalHoliday> LegalHoliday::from_ymd(const int year, const int month, const int day) {
        char buffer[9];
        snprintf(buffer, sizeof(buffer), "%04d%02d%02d", year, month, day);
        const regex re(string(buffer) + "[0-1][0-8][\\+|-]\\d{2}");
        if (smatch match; regex_search(DATA, match, re)) {
            const string data= match[0];
            return LegalHoliday(year, month, day, data);
        }
        return nullopt;
    }

    SolarDay LegalHoliday::get_day() const {
        return day;
    }

    SolarDay LegalHoliday::get_target() const {
        return target;
    }

    bool LegalHoliday::is_work() const {
        return work;
    }

    string LegalHoliday::get_name() const {
        return name;
    }

    string LegalHoliday::to_string() const {
        return day.to_string() + " " + name + "(" + (work ? "班" : "休") + ")";
    }

    optional<LegalHoliday> LegalHoliday::next(int n) const {
        int year = day.get_year();
        int month = day.get_month();
        if (n == 0) {
            return from_ymd(year, month, day.get_day()).value();
        }
        auto data = vector<string>();
        char buffer[9];
        snprintf(buffer, sizeof(buffer), "%04d%02d%02d", year, month, day.get_day());
        auto today = string(buffer);
        char buffer_y[5];
        snprintf(buffer_y, sizeof(buffer_y), "%04d", year);
        regex re(string(buffer_y) + R"(\d{4}[0-1][0-8][\+|-]\d{2})");
        smatch match;
        auto search_start(DATA.cbegin());
        while (regex_search(search_start, DATA.cend(), match, re)) {
            data.push_back(match[0]);
            search_start = match[0].second;
        }
        int index = -1;
        int size = static_cast<int>(data.size());
        for (int i = 0; i < size; i++) {
            if (equal(today.begin(), today.end(), data[i].begin())) {
                index = i;
                break;
            }
        }
        if (index == -1) {
            return nullopt;
        }
        index += n;
        int y = year;
        if (n > 0) {
            while (index >= size) {
                index -= size;
                y += 1;
                data.clear();
                snprintf(buffer_y, sizeof(buffer_y), "%04d", y);
                regex r(string(buffer_y) + R"(\d{4}[0-1][0-8][\+|-]\d{2})");
                auto start(DATA.cbegin());
                while (regex_search(start, DATA.cend(), match, r)) {
                    data.push_back(match[0]);
                    start = match[0].second;
                }
                size = static_cast<int>(data.size());
                if (size < 1) {
                    return nullopt;
                }
            }
        } else {
            while (index < 0) {
                y -= 1;
                data.clear();
                snprintf(buffer_y, sizeof(buffer_y), "%04d", y);
                regex r(string(buffer_y) + R"(\d{4}[0-1][0-8][\+|-]\d{2})");
                auto start(DATA.cbegin());
                while (regex_search(start, DATA.cend(), match, r)) {
                    data.push_back(match[0]);
                    start = match[0].second;
                }
                size = static_cast<int>(data.size());
                if (size < 1) {
                    return nullopt;
                }
                index += size;
            }
        }
        string d = data[index];
        return LegalHoliday(stoi(d.substr(0, 4)), stoi(d.substr(4, 2)), stoi(d.substr(6, 2)), d);
    }

    const vector<string> SolarFestival::NAMES = {
        "元旦", "三八妇女节", "植树节", "五一劳动节", "五四青年节", "六一儿童节", "建党节", "八一建军节", "教师节", "国庆节"
    };

    string SolarFestival::DATA = "@00001011950@01003081950@02003121979@03005011950@04005041950@05006011950@06007011941@07008011933@08009101985@09010011950";

    optional<SolarFestival> SolarFestival::from_index(const int year, const int index) {
        if (index < 0 || static_cast<size_t>(index) >= NAMES.size()) {
            throw invalid_argument("illegal index: " + std::to_string(index));
        }
        char buffer[3];
        snprintf(buffer, sizeof(buffer), "%02d", index);
        const regex re("@" + string(buffer) + "\\d+");
        if (smatch match; regex_search(DATA, match, re)) {
            FestivalType type;
            const string data = match[0];
            char d = data[3];
            if ('0' == d) {
                type = FestivalType::DAY;
            } else if ('1' == d) {
                type = FestivalType::TERM;
            } else if ('2' == d) {
                type = FestivalType::EVE;
            } else {
                return nullopt;
            }
            const int start_year = stoi(data.substr(8));
            if (year < start_year) {
                return nullopt;
            }
            return SolarFestival(type, SolarDay::from_ymd(year, stoi(data.substr(4, 2)), stoi(data.substr(6, 2))), start_year, data);
        }
        return nullopt;
    }

    optional<SolarFestival> SolarFestival::from_ymd(const int year, const int month, const int day) {
        char buffer[5];
        snprintf(buffer, sizeof(buffer), "%02d%02d", month, day);
        const regex re("@\\d{2}0" + string(buffer) + "\\d+");
        if (smatch match; regex_search(DATA, match, re)) {
            const string data= match[0];
            const int start_year = stoi(data.substr(8));
            if (year < start_year) {
                return nullopt;
            }
            return SolarFestival(FestivalType::DAY, SolarDay::from_ymd(year, month, day), start_year, data);
        }
        return nullopt;
    }

    optional<SolarFestival> SolarFestival::next(const int n) const {
        const int size = static_cast<int>(NAMES.size());
        const int i = index + n;
        return from_index((day.get_year() * size + i) / size, index_of(i, size));
    }

    FestivalType SolarFestival::get_type() const {
        return _type;
    }

    int SolarFestival::get_index() const {
        return index;
    }

    SolarDay SolarFestival::get_day() const {
        return day;
    }

    int SolarFestival::get_start_year() const {
        return start_year;
    }

    string SolarFestival::get_name() const {
        return name;
    }

    string SolarFestival::to_string() const {
        return day.to_string() + " " + name;
    }

    const vector<string> LunarFestival::NAMES = {
        "春节", "元宵节", "龙头节", "上巳节", "清明节", "端午节", "七夕节", "中元节", "中秋节", "重阳节", "冬至节", "腊八节", "除夕"
    };

    string LunarFestival::DATA = "@0000101@0100115@0200202@0300303@04107@0500505@0600707@0700715@0800815@0900909@10124@1101208@122";

    optional<LunarFestival> LunarFestival::from_index(const int year, const int index) {
        if (index < 0 || static_cast<size_t>(index) >= NAMES.size()) {
            throw invalid_argument("illegal index: " + std::to_string(index));
        }
        char buffer[3];
        snprintf(buffer, sizeof(buffer), "%02d", index);
        const regex re("@" + string(buffer) + "\\d+");
        if (smatch match; regex_search(DATA, match, re)) {
            const string data= match[0];
            const char d = data[3];
            if ('0' == d) {
                return LunarFestival(FestivalType::DAY, LunarDay::from_ymd(year, stoi(data.substr(4, 2)), stoi(data.substr(6))), nullopt, data);
            }
            if ('1' == d) {
                SolarTerm solar_term = SolarTerm::from_index(year, stoi(data.substr(4)));
                return LunarFestival(FestivalType::TERM, solar_term.get_solar_day().get_lunar_day(), solar_term, data);
            }
            if ('2' == d) {
                return LunarFestival(FestivalType::EVE, LunarDay::from_ymd(year + 1, 1, 1).next(-1), nullopt, data);
            }
        }
        return nullopt;
    }

    optional<LunarFestival> LunarFestival::from_ymd(int year, int month, int day) {
        char buffer[15];
        snprintf(buffer, sizeof(buffer), "@\\d{2}0%02d%02d", month, day);
        const auto pattern = string(buffer);
        const regex re(pattern);
        smatch match;
        if (regex_search(DATA, match, re)) {
            const string data= match[0];
            return LunarFestival(FestivalType::DAY, LunarDay::from_ymd(year, month, day), nullopt, data);
        }
        const regex re1("@\\d{2}1\\d{2}");
        auto start(DATA.cbegin());
        while (regex_search(start, DATA.cend(), match, re1)) {
            const string data= match[0];
            SolarTerm solar_term = SolarTerm::from_index(year, stoi(data.substr(4)));
            if (LunarDay lunar_day = solar_term.get_solar_day().get_lunar_day(); lunar_day.get_year() == year && lunar_day.get_month() == month && lunar_day.get_day() == day) {
                return LunarFestival(FestivalType::TERM, lunar_day, solar_term, data);
            }
            start = match[0].second;
        }
        if (const regex re2("@\\d{2}2"); regex_search(DATA, match, re2)) {
            LunarDay lunar_day = LunarDay::from_ymd(year, month, day);
            if (LunarDay next_day = lunar_day.next(1); next_day.get_month() == 1 && next_day.get_day() == 1) {
                return LunarFestival(FestivalType::EVE, lunar_day, nullopt, match[0]);
            }
        }
        return nullopt;
    }

    optional<LunarFestival> LunarFestival::next(const int n) const {
        const int size = static_cast<int>(NAMES.size());
        const int i = index + n;
        return from_index((day.get_year() * size + i) / size, index_of(i, size));
    }

    FestivalType LunarFestival::get_type() const {
        return _type;
    }

    int LunarFestival::get_index() const {
        return index;
    }

    LunarDay LunarFestival::get_day() const {
        return day;
    }

    optional<SolarTerm> LunarFestival::get_solar_term() const {
        return solar_term;
    }

    string LunarFestival::get_name() const {
        return name;
    }

    string LunarFestival::to_string() const {
        return day.to_string() + " " + name;
    }

    RabByungElement RabByungElement::from_index(const int index) {
        return RabByungElement(index);
    }

    RabByungElement RabByungElement::from_name(const string &name) {
        return RabByungElement(name);
    }

    RabByungElement RabByungElement::next(const int n) const {
        return from_index(next_index(n));
    }

    bool RabByungElement::equals(const RabByungElement &other) const {
        return to_string() == other.to_string();
    }

    RabByungElement RabByungElement::get_reinforce() const {
        return next(1);
    }

    RabByungElement RabByungElement::get_restrain() const {
        return next(2);
    }

    RabByungElement RabByungElement::get_reinforced() const {
        return next(-1);
    }

    RabByungElement RabByungElement::get_restrained() const {
        return next(-2);
    }

    Direction RabByungElement::get_direction() const {
        const int indices[5] = {2, 8, 4, 6, 0};
        return Direction::from_index(indices[index]);
    }

    string RabByungElement::get_name() const {
        return regex_replace(names[index], regex("金"), "铁");
    }

    RabByungYear RabByungYear::from_sixty_cycle(const int rab_byung_index, const SixtyCycle& sixty_cycle) {
        return RabByungYear(rab_byung_index, sixty_cycle);
    }

    RabByungYear RabByungYear::from_element_zodiac(int rab_byung_index, const RabByungElement& element, const Zodiac& zodiac) {
        for (int i = 0; i < 60; i++) {
            if (SixtyCycle sixty_cycle = SixtyCycle::from_index(i); sixty_cycle.get_earth_branch().get_zodiac().equals(zodiac) && sixty_cycle.get_heaven_stem().get_element().get_index() == element.get_index()) {
                return RabByungYear(rab_byung_index, sixty_cycle);
            }
        }
        throw invalid_argument("illegal rab-byung element " + element.to_string() + ", zodiac " + zodiac.to_string());
    }

    RabByungYear RabByungYear::from_year(int year) {
        return RabByungYear((year - 1024) / 60, SixtyCycle::from_index(year - 4));
    }

    int RabByungYear::get_rab_byung_index() const {
        return rab_byung_index;
    }

    SixtyCycle RabByungYear::get_sixty_cycle() const {
        return sixty_cycle;
    }

    Zodiac RabByungYear::get_zodiac() const {
        return sixty_cycle.get_earth_branch().get_zodiac();
    }

    RabByungElement RabByungYear::get_element() const {
        return RabByungElement::from_index(sixty_cycle.get_heaven_stem().get_element().get_index());
    }

    string RabByungYear::get_name() const {
        const string digits[] = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
        const string units[] = {"", "十", "百"};
        int n = rab_byung_index + 1;
        string s;
        int pos = 0;
        while (n > 0) {
            if (const int digit = n % 10; digit > 0) {
                s.insert(0, digits[digit] + units[pos]);
            } else if (!s.empty()) {
                s.insert(0, digits[digit]);
            }
            n /= 10;
            pos++;
        }
        if (s.rfind("一十", 0) == 0) {
            const string target = "一";
            s.erase(0, target.length());
        }
        return "第" + s + "饶迥" + get_element().to_string() + get_zodiac().to_string() + "年";
    }

    RabByungYear RabByungYear::next(const int n) const {
        return from_year(get_year() + n);
    }

    int RabByungYear::get_year() const {
        return 1024 + rab_byung_index * 60 + sixty_cycle.get_index();
    }

    int RabByungYear::get_leap_month() const {
        int y = 1;
        int m = 4;
        int t = 0;
        const int current_year = get_year();
        while (y < current_year) {
            const int i = m - 1 + (t % 2 == 0 ? 33 : 32);
            y = (y * 12 + i) / 12;
            m = i % 12 + 1;
            t++;
        }
        return y == current_year ? m : 0;
    }

    SolarYear RabByungYear::get_solar_year() const {
        return SolarYear::from_year(get_year());
    }

    int RabByungYear::get_month_count() const {
        return get_leap_month() < 1 ? 12 : 13;
    }

    RabByungMonth RabByungYear::get_first_month() const {
        return RabByungMonth(*this, 1);
    }

    vector<RabByungMonth> RabByungYear::get_months() const {
        auto l = vector<RabByungMonth>();
        const int leap_month = get_leap_month();
        for (int i = 1; i < 13; i++) {
            l.emplace_back(*this, i);
            if (i == leap_month) {
                l.emplace_back(*this, -i);
            }
        }
        return l;
    }

    std::map<int, vector<int>> RabByungMonth::DAYS = std::map<int, vector<int>>();

    const vector<string> RabByungMonth::NAMES = {
        "正月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"
    };

    const vector<string> RabByungMonth::ALIAS = {
        "神变月", "苦行月", "具香月", "萨嘎月", "作净月", "明净月", "具醉月", "具贤月", "天降月", "持众月", "庄严月", "满意月"
    };

    RabByungMonth RabByungMonth::from_ym(const int year, const int month) {
        return RabByungMonth(year, month);
    }

    RabByungMonth RabByungMonth::from_element_zodiac(const int rab_byung_index, const RabByungElement& element, const Zodiac& zodiac, const int month) {
        return RabByungMonth(rab_byung_index, element, zodiac, month);
    }

    RabByungYear RabByungMonth::get_rab_byung_year() const {
        return year;
    }

    int RabByungMonth::get_year() const {
        return year.get_year();
    }

    int RabByungMonth::get_month() const {
        return month;
    }

    int RabByungMonth::get_month_with_leap() const {
        return leap ? -month : month;
    }

    int RabByungMonth::get_index_in_year() const {
        return index_in_year;
    }

    bool RabByungMonth::is_leap() const {
        return leap;
    }

    string RabByungMonth::get_name() const {
        return (leap ? "闰" : "") + NAMES[month - 1];
    }

    string RabByungMonth::get_alias() const {
        return (leap ? "闰" : "") + ALIAS[month - 1];
    }

    string RabByungMonth::to_string() const {
        return year.to_string() + get_name();
    }

    RabByungMonth RabByungMonth::next(const int n) const {
        if (n == 0) {
            return from_ym(get_year(), get_month_with_leap());
        }
        int m = index_in_year + 1 + n;
        RabByungYear y = year;
        if (n > 0) {
            int month_count = y.get_month_count();
            while (m > month_count) {
                m -= month_count;
                y = y.next(1);
                month_count = y.get_month_count();
            }
        } else {
            while (m <= 0) {
                y = y.next(-1);
                m += y.get_month_count();
            }
        }
        bool leap = false;
        if (const int leap_month = y.get_leap_month(); leap_month > 0) {
            if (m == leap_month + 1) {
                leap = true;
            }
            if (m > leap_month) {
                m--;
            }
        }
        return from_ym(y.get_year(), leap ? -m : m);
    }

    int RabByungMonth::get_day_count() const {
        return 30 + static_cast<int>(get_leap_days().size()) - static_cast<int>(get_miss_days().size());
    }

    vector<int> RabByungMonth::get_special_days() const {
        if (const auto it = DAYS.find(get_year() * 13 + get_index_in_year()); it != DAYS.end()) {
            return it->second;
        }
        return {};
    }

    vector<int> RabByungMonth::get_leap_days() const {
        auto l = vector<int>();
        const auto days = get_special_days();
        for (const int d : days) {
            if (d > 0) {
                l.push_back(d);
            }
        }
        return l;
    }

    vector<int> RabByungMonth::get_miss_days() const {
        auto l = vector<int>();
        const auto days = get_special_days();
        for (const int d : days) {
            if (d < 0) {
                l.push_back(-d);
            }
        }
        return l;
    }

    bool RabByungMonth::equals(const RabByungMonth &other) const {
        return to_string() == other.to_string();
    }

    RabByungDay RabByungMonth::get_first_day() const {
        return RabByungDay(*this, 1);
    }

    vector<RabByungDay> RabByungMonth::get_days() const {
        auto l = vector<RabByungDay>();
        auto miss_days = get_miss_days();
        auto leap_days = get_leap_days();
        for (int i = 1; i < 31; i++) {
            if (find(miss_days.begin(), miss_days.end(), i) != miss_days.end()) {
                continue;
            }
            l.emplace_back(*this, i);
            if (find(leap_days.begin(), leap_days.end(), i) != leap_days.end()) {
                l.emplace_back(*this, -i);
            }
        }
        return l;
    }

    const vector<string> RabByungDay::NAMES = {
        "初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十", "十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十", "廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十"
    };

    RabByungDay RabByungDay::from_ymd(const int year, const int month, const int day) {
        return RabByungDay(year, month, day);
    }

    RabByungDay RabByungDay::from_element_zodiac(const int rab_byung_index, const RabByungElement &element, const Zodiac &zodiac, const int month, const int day) {
        return RabByungDay(rab_byung_index, element, zodiac, month, day);
    }

    RabByungDay RabByungDay::from_solar_day(const SolarDay& solar_day) {
        int days = solar_day.subtract(SolarDay::from_ymd(1951, 1, 8));
        RabByungMonth m = RabByungMonth::from_ym(1950, 12);
        int count = m.get_day_count();
        while (days >= count) {
            days -= count;
            m = m.next(1);
            count = m.get_day_count();
        }
        int day = days + 1;
        for (const int d : m.get_special_days()) {
            if (d < 0) {
                if (day >= -d) {
                    day++;
                }
            } else if (d > 0) {
                if (day == d + 1) {
                    day = -d;
                    break;
                }
                if (day > d + 1) {
                    day--;
                }
            }
        }
        return RabByungDay(m, day);
    }

    RabByungMonth RabByungDay::get_rab_byung_month() const {
        return month;
    }

    int RabByungDay::get_year() const {
        return month.get_year();
    }

    int RabByungDay::get_month() const {
        return month.get_month_with_leap();
    }

    int RabByungDay::get_day() const {
        return day;
    }

    bool RabByungDay::is_leap() const {
        return leap;
    }

    int RabByungDay::get_day_with_leap() const {
        return leap ? -day : day;
    }

    string RabByungDay::get_name() const {
        return (leap ? "闰" : "") + NAMES[day - 1];
    }

    string RabByungDay::to_string() const {
        return month.to_string() + get_name();
    }

    int RabByungDay::subtract(const RabByungDay& target) const {
        return get_solar_day().subtract(target.get_solar_day());
    }

    SolarDay RabByungDay::get_solar_day() const {
        RabByungMonth m = RabByungMonth::from_ym(1950, 12);
        int n = 0;
        while (!month.equals(m)) {
            n += m.get_day_count();
            m = m.next(1);
        }
        int t = day;
        for (const int d : m.get_special_days()) {
            if (d < 0) {
                if (t > -d) {
                    t--;
                }
            } else if (d > 0) {
                if (t > d) {
                    t++;
                }
            }
        }
        if (leap) {
            t++;
        }
        return SolarDay::from_ymd(1951, 1, 7).next(n + t);
    }

    RabByungDay RabByungDay::next(const int n) const {
        return get_solar_day().next(n).get_rab_byung_day();
    }
}
